/*-------------------------------------------------------------------------
 *
 * rewriteHandler.c
 *		查询重写的主模块。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/backend/rewrite/rewriteHandler.c
 *
 * NOTES
 *	  Some of the terms used in this file are of historic nature: "retrieve"
 *	  was the PostQUEL keyword for what today is SELECT. "RIR" stands for
 *	  "Retrieve-Instead-Retrieve", that is an ON SELECT DO INSTEAD SELECT rule
 *	  (which has to be unconditional and where only one rule can exist on each
 *	  relation).
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/relation.h"
#include "access/sysattr.h"
#include "access/table.h"
#include "catalog/dependency.h"
#include "catalog/pg_type.h"
#include "commands/trigger.h"
#include "executor/executor.h"
#include "foreign/fdwapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/optimizer.h"
#include "parser/analyze.h"
#include "parser/parse_coerce.h"
#include "parser/parse_relation.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteHandler.h"
#include "rewrite/rewriteManip.h"
#include "rewrite/rewriteSearchCycle.h"
#include "rewrite/rowsecurity.h"
#include "tcop/tcopprot.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"


/* 我们使用这些的列表来检测RewriteQuery中的递归 */
typedef struct rewrite_event
{
	Oid			relation;		/* 具有规则的关系的OID */
	CmdType		event;			/* 被触发的规则的类型 */
} rewrite_event;

typedef struct acquireLocksOnSubLinks_context
{
	bool		for_execute;	/* AcquireRewriteLocks的forExecute参数 */
} acquireLocksOnSubLinks_context;

typedef struct fireRIRonSubLink_context
{
	List	   *activeRIRs;
	bool		hasRowSecurity;
} fireRIRonSubLink_context;

static bool fc_acquireLocksOnSubLinks(Node *fc_node,
								   acquireLocksOnSubLinks_context *fc_context);
static Query *fc_rewriteRuleAction(Query *fc_parsetree,
								Query *fc_rule_action,
								Node *fc_rule_qual,
								int fc_rt_index,
								CmdType fc_event,
								bool *fc_returning_flag);
static List *fc_adjustJoinTreeList(Query *fc_parsetree, bool fc_removert, int fc_rt_index);
static List *fc_rewriteTargetListIU(List *fc_targetList,
								 CmdType fc_commandType,
								 OverridingKind fc_override,
								 Relation fc_target_relation,
								 RangeTblEntry *fc_values_rte,
								 int fc_values_rte_index,
								 Bitmapset **fc_unused_values_attrnos);
static TargetEntry *fc_process_matched_tle(TargetEntry *fc_src_tle,
										TargetEntry *fc_prior_tle,
										const char *fc_attrName);
static Node *get_assignment_input(Node *fc_node);
static Bitmapset *fc_findDefaultOnlyColumns(RangeTblEntry *fc_rte);
static bool fc_rewriteValuesRTE(Query *fc_parsetree, RangeTblEntry *fc_rte, int fc_rti,
							 Relation fc_target_relation,
							 Bitmapset *fc_unused_cols);
static void fc_rewriteValuesRTEToNulls(Query *fc_parsetree, RangeTblEntry *fc_rte);
static void fc_markQueryForLocking(Query *fc_qry, Node *fc_jtnode,
								LockClauseStrength fc_strength, LockWaitPolicy fc_waitPolicy,
								bool fc_pushedDown);
static List *fc_matchLocks(CmdType fc_event, RuleLock *fc_rulelocks,
						int fc_varno, Query *fc_parsetree, bool *fc_hasUpdate);
static Query *fc_fireRIRrules(Query *fc_parsetree, List *fc_activeRIRs);
static bool fc_view_has_instead_trigger(Relation fc_view, CmdType fc_event);
static Bitmapset *fc_adjust_view_column_set(Bitmapset *fc_cols, List *fc_targetlist);



void AcquireRewriteLocks(Query *fc_parsetree,
					bool fc_forExecute,
					bool fc_forUpdatePushedDown)
{
	ListCell   *fc_l;
	int			fc_rt_index;
	acquireLocksOnSubLinks_context fc_context;

	fc_context.for_execute = fc_forExecute;

	/*
	 * 首先，处理当前查询级别的 RTEs。
	 */
	fc_rt_index = 0;
	foreach(fc_l, fc_parsetree->rtable)
	{
		RangeTblEntry *fc_rte = (RangeTblEntry *) lfirst(fc_l);
		Relation	fc_rel;
		LOCKMODE	fc_lockmode;
		List	   *fc_newaliasvars;
		Index		fc_curinputvarno;
		RangeTblEntry *fc_curinputrte;
		ListCell   *fc_ll;

		++fc_rt_index;
		switch (fc_rte->rtekind)
		{
			case RTE_RELATION:

				/*
				 * 获取关系的适当锁类型，并且不要在事务结束之前释放它。
				 * 这保护了重写器、规划器和执行器免受查询过程中模式变更的影响。
				 *
				 * 如果 forExecute 为 false，则忽略 rellockmode，直接使用
				 * AccessShareLock。
				 */
				if (!fc_forExecute)
					fc_lockmode = AccessShareLock;
				else if (fc_forUpdatePushedDown)
				{
					/* 升级 RTE 的锁模式以反映下推锁定 */
					if (fc_rte->rellockmode == AccessShareLock)
						fc_rte->rellockmode = RowShareLock;
					fc_lockmode = fc_rte->rellockmode;
				}
				else
					fc_lockmode = fc_rte->rellockmode;

				fc_rel = table_open(fc_rte->relid, fc_lockmode);

				/*
				 * 在我们打开关系的同时，更新 RTE 的 relkind，
				 * 以防自规则制定以来发生了变化。
				 */
				fc_rte->relkind = fc_rel->rd_rel->relkind;

				table_close(fc_rel, NoLock);
				break;

			case RTE_JOIN:

				/*
				 * 扫描连接的别名变量列表，以查看是否有任何列已被删除，
				 * 如果有，则用 null 指针替换这些 Vars。
				 *
				 * 由于连接只有两个输入，我们可以期望看到
				 * 对同一输入 RTE 的多个引用；消除多次获取。
				 */
				fc_newaliasvars = NIL;
				fc_curinputvarno = 0;
				fc_curinputrte = NULL;
				foreach(fc_ll, fc_rte->joinaliasvars)
				{
					Var		   *fc_aliasitem = (Var *) lfirst(fc_ll);
					Var		   *fc_aliasvar = fc_aliasitem;

					/* 检查任何隐式强制转换 */
					fc_aliasvar = (Var *) strip_implicit_coercions((Node *) fc_aliasvar);

					/*
					 * 如果列表项不是一个简单的 Var，则它必须
					 * 代表一个合并列，即 USING 列，因此它
					 * 不能被删除，因为它在连接条件中被引用。
					 * （可能它已经是个 null 指针？但这也没关系。）
					 */
					if (fc_aliasvar && IsA(fc_aliasvar, Var))
					{
						/*
						 * 别名列表的元素必须引用
						 * 相同 rtable 的早期 RTE，因为那是
						 * 规划器构建事物的顺序。因此我们已经
						 * 处理了引用的 RTE，所以安全地
						 * 在其上使用 get_rte_attribute_is_dropped。
						 * （重写或规划后这可能不成立，但这里假设是可以的。）
						 */
						Assert(fc_aliasvar->varlevelsup == 0);
						if (fc_aliasvar->varno != fc_curinputvarno)
						{
							fc_curinputvarno = fc_aliasvar->varno;
							if (fc_curinputvarno >= fc_rt_index)
								elog(ERROR, "unexpected varno %d in JOIN RTE %d",
									 fc_curinputvarno, fc_rt_index);
							fc_curinputrte = rt_fetch(fc_curinputvarno,
												   fc_parsetree->rtable);
						}
						if (get_rte_attribute_is_dropped(fc_curinputrte,
														 fc_aliasvar->varattno))
						{
							/* 用 NULL 替换连接别名项 */
							fc_aliasitem = NULL;
						}
					}
					fc_newaliasvars = lappend(fc_newaliasvars, fc_aliasitem);
				}
				fc_rte->joinaliasvars = fc_newaliasvars;
				break;

			case RTE_SUBQUERY:

				/*
				 * 子查询 RTE 本身是可以的，但我们必须
				 * 递归处理表示的子查询。
				 */
				AcquireRewriteLocks(fc_rte->subquery,
									fc_forExecute,
									(fc_forUpdatePushedDown ||
									 get_parse_rowmark(fc_parsetree, fc_rt_index) != NULL));
				break;

			default:
				/* 忽略其他类型的 RTEs */
				break;
		}
	}

	/* 递归进入 WITH 中的子查询 */
	foreach(fc_l, fc_parsetree->cteList)
	{
		CommonTableExpr *fc_cte = (CommonTableExpr *) lfirst(fc_l);

		AcquireRewriteLocks((Query *) fc_cte->ctequery, fc_forExecute, false);
	}

	/*
	 * 也递归进入子链接子查询。但我们已经处理了
	 * rtable 和 cteList 中的子查询。
	 */
	if (fc_parsetree->hasSubLinks)
		query_tree_walker(fc_parsetree, fc_acquireLocksOnSubLinks, &fc_context,
						  QTW_IGNORE_RC_SUBQUERIES);
}

/*
 * Walker 用于查找 AcquireRewriteLocks 的子链接子查询
 */
static bool fc_acquireLocksOnSubLinks(Node *fc_node, acquireLocksOnSubLinks_context *fc_context)
{
	if (fc_node == NULL)
		return false;
	if (IsA(fc_node, SubLink))
	{
		SubLink    *fc_sub = (SubLink *) fc_node;

		/* 做我们来这里的目的 */
		AcquireRewriteLocks((Query *) fc_sub->subselect,
							fc_context->for_execute,
							false);
		/* 继续处理 SubLink 的左参数 */
	}

	/*
	 * 不要递归进入 Query 节点，因为 AcquireRewriteLocks 已经
	 * 为我们处理了子选择中的子选择。
	 */
	return expression_tree_walker(fc_node, fc_acquireLocksOnSubLinks, fc_context);
}


/*
 * rewriteRuleAction -
 *	  用适当的限定符重写规则操作（提取自
 *	  触发查询）。
 *
 * 输入参数：
 *	parsetree - 原始查询
 *	rule_action - 规则的一项操作（查询）
 *	rule_qual - 规则的 WHERE 条件，或 NULL 如果没有条件
 *	rt_index - 原始查询中结果关系的 RT 索引
 *	event - 规则事件的类型
 * 输出参数：
 *	*returning_flag - 如果我们在 rule_action 中重写 RETURNING 子句
 *					（必须初始化为 false），则设置为 true
 * 返回值：
 *	重写的规则操作形式
 */
static Query * fc_rewriteRuleAction(Query *fc_parsetree,
				  Query *fc_rule_action,
				  Node *fc_rule_qual,
				  int fc_rt_index,
				  CmdType fc_event,
				  bool *fc_returning_flag)
{
	int			fc_current_varno,
				fc_new_varno;
	int			fc_rt_length;
	Query	   *fc_sub_action;
	Query	  **fc_sub_action_ptr;
	acquireLocksOnSubLinks_context fc_context;
	ListCell   *fc_lc;

	fc_context.for_execute = true;

	/*
	 * 制作 rule action 和 qual 的可修改副本（我们得到的是
	 * relcache 中存储的版本；不要触碰它们！）。
	 */
	fc_rule_action = copyObject(fc_rule_action);
	fc_rule_qual = copyObject(fc_rule_qual);

	/*
	 * 获取必要的锁并修正任何已删除的 JOIN RTE 条目。
	 */
	AcquireRewriteLocks(fc_rule_action, true, false);
	(void) fc_acquireLocksOnSubLinks(fc_rule_qual, &fc_context);

	fc_current_varno = fc_rt_index;
	fc_rt_length = list_length(fc_parsetree->rtable);
	fc_new_varno = PRS2_NEW_VARNO + fc_rt_length;

	/*
	 * 调整规则操作和 qual 的 varnos，以便我们能够合并
	 * 它的 rtable 与主 parsetree 的 rtable。
	 *
	 * 如果规则操作是 INSERT...SELECT，那么 OLD/NEW rtable 条目
	 * 将位于 SELECT 部分，我们必须修改该部分而不是
	 * 顶层 INSERT（障碍！）。
	 */
	fc_sub_action = getInsertSelectQuery(fc_rule_action, &fc_sub_action_ptr);

	OffsetVarNodes((Node *) fc_sub_action, fc_rt_length, 0);
	OffsetVarNodes(fc_rule_qual, fc_rt_length, 0);
	/* 但对 OLD 的引用应指向原始的 rt_index */
	ChangeVarNodes((Node *) fc_sub_action,
				   PRS2_OLD_VARNO + fc_rt_length, fc_rt_index, 0);
	ChangeVarNodes(fc_rule_qual,
				   PRS2_OLD_VARNO + fc_rt_length, fc_rt_index, 0);

	/*
	 * 如果规则动作中的任何子查询 RTE 包含引用当前查询级别的变量（对 NEW/OLD 的引用），
	 * 则将其标记为 LATERAL。那些确实是横向引用，但我们历史上没有要求用户显式地标记
	 * 这样的子查询为 LATERAL。然而，如果在非 LATERAL 子查询中存在这样的变量，规划器
	 * 会抱怨，因此我们必须在此修复。
	 */
	foreach(fc_lc, fc_sub_action->rtable)
	{
		RangeTblEntry *fc_rte = (RangeTblEntry *) lfirst(fc_lc);

		if (fc_rte->rtekind == RTE_SUBQUERY && !fc_rte->lateral &&
			contain_vars_of_level((Node *) fc_rte->subquery, 1))
			fc_rte->lateral = true;
	}

	/*
	 * 生成扩展的 rtable，包括主解析树的 rtable 加上规则动作的 rtable；
	 * 这成为规则动作的完整 rtable。在我们完成重写后，一些条目可能会未使用，
	 * 但我们出于两个原因保留它们：
	 *
	 * 如果我们选择性地删除 RT 条目，将更加困难于调整查询的 varnos。
	 *
	 * 如果规则是 INSTEAD，则原始查询根本不会执行，因此必须保留其 rtable
	 * 以便执行器能够正确进行权限检查。
	 *
	 * 在完成的联合树中未引用的 RT 条目将被规划器忽略，因此它们不会影响查询语义。
	 * 但是，它们中指定的任何权限检查将在执行器启动期间应用（见 ExecCheckRTEPerms()）。
	 * 这使我们能够检查调用者是否具有，例如，对视图的插入权限，即使该视图在
	 * 结果查询中根本未被语义引用。
	 *
	 * 当规则不是 INSTEAD 时，对其复制的 RT 条目进行的权限检查将与在原始查询执行
	 * 期间进行的权限检查冗余，但我们不在意单独处理该情况。
	 *
	 * 注意：因为规划器会破坏性地更改 rtable，我们必须确保规则动作的 rtable 是独立的
	 * 并且与主 rtable 没有共享子结构。因此，在这里进行深拷贝。
	 *
	 * 还要注意，RewriteQuery() 依赖于原始查询的 RT 条目出现在扩展 rtable 的开头，
	 * 所以要小心更改这一点。
	 */
	fc_sub_action->rtable = list_concat(copyObject(fc_parsetree->rtable),
									 fc_sub_action->rtable);

	/*
	 * parsetree 的 rtable 中可能有一些 SubLinks，在这种情况下，我们最好正确标记
	 * sub_action。
	 */
	if (fc_parsetree->hasSubLinks && !fc_sub_action->hasSubLinks)
	{
		foreach(fc_lc, fc_parsetree->rtable)
		{
			RangeTblEntry *fc_rte = (RangeTblEntry *) lfirst(fc_lc);

			switch (fc_rte->rtekind)
			{
				case RTE_RELATION:
					fc_sub_action->hasSubLinks =
						checkExprHasSubLink((Node *) fc_rte->tablesample);
					break;
				case RTE_FUNCTION:
					fc_sub_action->hasSubLinks =
						checkExprHasSubLink((Node *) fc_rte->functions);
					break;
				case RTE_TABLEFUNC:
					fc_sub_action->hasSubLinks =
						checkExprHasSubLink((Node *) fc_rte->tablefunc);
					break;
				case RTE_VALUES:
					fc_sub_action->hasSubLinks =
						checkExprHasSubLink((Node *) fc_rte->values_lists);
					break;
				default:
					/* 其他 RTE 类型不包含裸表达式 */
					break;
			}
			fc_sub_action->hasSubLinks |=
				checkExprHasSubLink((Node *) fc_rte->securityQuals);
			if (fc_sub_action->hasSubLinks)
				break;			/* 不需要继续扫描 rtable */
		}
	}

	/*
	 * 此外，我们可能已经将一些带有 RLS 条件的 RTE 吸收到 sub_action 中。
	 * 如果是这样，无论这些 RTE 是否将在我们完成重写后被引用，都要将其标记为 hasRowSecurity。
	 * （注意：当前这是一个无操作，因为 RLS 条件直到稍后才添加，但这样做似乎对将来
	 * 有好处。）
	 */
	fc_sub_action->hasRowSecurity |= fc_parsetree->hasRowSecurity;

	/*
	 * 每个规则动作的联合树应为主解析树的联合树加上该规则的联合树，但通常是
	 * *不带* 我们正在替换的原始 rtindex（如果存在，对于 INSERT 来说不会有）。
	 * 注意，如果规则动作引用 OLD，它的联合树将添加对 rt_index 的引用。
	 * 如果规则动作不引用 OLD，但规则限定或用户查询限定引用了 OLD，
	 * 那么我们需要在联合树中保留原始 rtindex 以提供对限定的支持。
	 * 然而，我们不希望原始 rtindex 被连接两次，因此如果规则动作提到它，
	 * 则避免保留它。
	 *
	 * 如上所述，动作的联合树不能与主解析树共享子结构。
	 */
	if (fc_sub_action->commandType != CMD_UTILITY)
	{
		bool		fc_keeporig;
		List	   *fc_newjointree;

		Assert(fc_sub_action->jointree != NULL);
		fc_keeporig = (!rangeTableEntry_used((Node *) fc_sub_action->jointree,
										  fc_rt_index, 0)) &&
			(rangeTableEntry_used(fc_rule_qual, fc_rt_index, 0) ||
			 rangeTableEntry_used(fc_parsetree->jointree->quals, fc_rt_index, 0));
		fc_newjointree = fc_adjustJoinTreeList(fc_parsetree, !fc_keeporig, fc_rt_index);
		if (fc_newjointree != NIL)
		{
			/*
			 * 如果 sub_action 是 setop，操作其联合树完全没有好处，因为联合树是虚拟的。
			 * （也许有一天我们可以将连接和限定条件下推到 setop 的成员语句中？）
			 */
			if (fc_sub_action->setOperations != NULL)
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));

			fc_sub_action->jointree->fromlist =
				list_concat(fc_newjointree, fc_sub_action->jointree->fromlist);

			/*
			 * 在新关节树中可能有一些子链接，在这种情况下，我们最好正确标记子动作。
			 */
			if (fc_parsetree->hasSubLinks && !fc_sub_action->hasSubLinks)
				fc_sub_action->hasSubLinks =
					checkExprHasSubLink((Node *) fc_newjointree);
		}
	}

	/*
	 * 如果原始查询有任何CTEs，将它们复制到规则动作中。但是对于一个实用程序动作，我们不需要它们。
	 */
	if (fc_parsetree->cteList != NIL && fc_sub_action->commandType != CMD_UTILITY)
	{
		/*
		 * 令人烦恼的实现限制：因为CTEs是在cteList中通过名称识别的，如果原始查询中的CTE与规则动作中的任何CTE同名，我们就不能合并该CTE。
		 *
		 * 这可能通过使用某种内部生成的ID来修复，而不是使用名称，将CTE RTE链接到它们的CTE。然而，反编译结果将相当混乱；注意下面hasRecursive标志的合并，这可能会改变这种冗余命名CTEs的明显语义。
		 */
		foreach(fc_lc, fc_parsetree->cteList)
		{
			CommonTableExpr *fc_cte = (CommonTableExpr *) lfirst(fc_lc);
			ListCell   *fc_lc2;

			foreach(fc_lc2, fc_sub_action->cteList)
			{
				CommonTableExpr *fc_cte2 = (CommonTableExpr *) lfirst(fc_lc2);

				if (strcmp(fc_cte->ctename, fc_cte2->ctename) == 0)
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("WITH query name \"%s\" appears in both a rule action and the query being rewritten",
									fc_cte->ctename)));
			}
		}

		/* 好的，可以安全地合并CTE列表 */
		fc_sub_action->cteList = list_concat(fc_sub_action->cteList,
										  copyObject(fc_parsetree->cteList));
		/* ... 并且不要忘记相关的标志 */
		fc_sub_action->hasRecursive |= fc_parsetree->hasRecursive;
		fc_sub_action->hasModifyingCTE |= fc_parsetree->hasModifyingCTE;

		/*
		 * 如果rule_action与sub_action不同（即规则动作是INSERT...SELECT），那么我们可能刚刚添加了一些在顶级查询级别下不允许的数据修改CTEs。这是解析器不允许的，我们在这里也不能生成这样的树，因此抛出一个错误。
		 *
		 * 可以想象， 通过将原始查询的CTEs附加到rule_action而不是sub_action， 可以支持这种情况。但做到这一点，我们必须在从原始查询复制的RTEs和SubLinks中递增ctelevelsup。目前看来，这样做并不值得。
		 */
		if (fc_sub_action->hasModifyingCTE && fc_rule_action != fc_sub_action)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("INSERT...SELECT rule actions are not supported for queries having data-modifying statements in WITH")));
	}

	/*
	 * 事件资格强制复制解析树并拆分为两个查询，一个带规则资格，一个带NOT规则资格。同时将用户查询资格添加到规则动作中
	 */
	AddQual(fc_sub_action, fc_rule_qual);

	AddQual(fc_sub_action, fc_parsetree->jointree->quals);

	/*
	 * 使用插入/更新中适当字段名称的目标列表条目的右侧重写new.attribute。
	 *
	 * KLUPE警告：由于ReplaceVarsFromTargetList返回的是一个变异副本，我们不能仅仅将其应用于sub_action；我们必须记住更新规则动作中的子链接。
	 */
	if ((fc_event == CMD_INSERT || fc_event == CMD_UPDATE) &&
		fc_sub_action->commandType != CMD_UTILITY)
	{
		fc_sub_action = (Query *)
			ReplaceVarsFromTargetList((Node *) fc_sub_action,
									  fc_new_varno,
									  0,
									  rt_fetch(fc_new_varno, fc_sub_action->rtable),
									  fc_parsetree->targetList,
									  (fc_event == CMD_UPDATE) ?
									  REPLACEVARS_CHANGE_VARNO :
									  REPLACEVARS_SUBSTITUTE_NULL,
									  fc_current_varno,
									  NULL);
		if (fc_sub_action_ptr)
			*fc_sub_action_ptr = fc_sub_action;
		else
			fc_rule_action = fc_sub_action;
	}

	/*
	 * 如果rule_action有一个RETURNING子句，那么如果触发查询没有RETURNING子句则将其丢弃，或者重写它以发出触发查询的RETURNING子句所要求的内容。如果超过一个规则有RETURNING子句，则抛出错误。
	 */
	if (!fc_parsetree->returningList)
		fc_rule_action->returningList = NIL;
	else if (fc_rule_action->returningList)
	{
		if (*fc_returning_flag)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("cannot have RETURNING lists in multiple rules")));
		*fc_returning_flag = true;
		fc_rule_action->returningList = (List *)
			ReplaceVarsFromTargetList((Node *) fc_parsetree->returningList,
									  fc_parsetree->resultRelation,
									  0,
									  rt_fetch(fc_parsetree->resultRelation,
											   fc_parsetree->rtable),
									  fc_rule_action->returningList,
									  REPLACEVARS_REPORT_ERROR,
									  0,
									  &fc_rule_action->hasSubLinks);

		/*
		 * 在parsetree的returningList中可能有一些SubLinks，在这种情况下，我们最好正确标记规则动作。
		 */
		if (fc_parsetree->hasSubLinks && !fc_rule_action->hasSubLinks)
			fc_rule_action->hasSubLinks =
				checkExprHasSubLink((Node *) fc_rule_action->returningList);
	}

	return fc_rule_action;
}

/*
 * 复制查询的关节树列表，并可选地尝试移除给定rt_index作为顶级连接项的任何出现（我们不在连接项内寻找它；这是可以的，因为我们只期望将其作为UPDATE或DELETE目标关系，这将在连接的顶层）。返回修改后的关节树列表---这是一个与原始树共享节点的单独副本。
 */
static List * fc_adjustJoinTreeList(Query *fc_parsetree, bool fc_removert, int fc_rt_index)
{
	List	   *fc_newjointree = copyObject(fc_parsetree->jointree->fromlist);
	ListCell   *fc_l;

	if (fc_removert)
	{
		foreach(fc_l, fc_newjointree)
		{
			RangeTblRef *fc_rtr = lfirst(fc_l);

			if (IsA(fc_rtr, RangeTblRef) &&
				fc_rtr->rtindex == fc_rt_index)
			{
				fc_newjointree = foreach_delete_current(fc_newjointree, fc_l);
				break;
			}
		}
	}
	return fc_newjointree;
}



/*
 * rewriteTargetListIU - 将INSERT/UPDATE目标列表重写为标准形式
 *
 * 这有以下职责：
 *
 * 1. 对于INSERT，添加目标列表条目以计算任何具有默认值且在给定目标列表中未分配的属性的默认值。
 * （我们不会为没有默认值的属性插入任何内容。然而，计划者稍后将为它们插入NULL，但没有理由通过额外的目标列表节点来减缓重写器处理的速度。）此外，对于INSERT和UPDATE，将显式的DEFAULT规范替换为列的默认表达式。
 *
 * 2. 合并多个相同目标属性的条目，或者在无法合并时声明错误。多个条目仅允许在INSERT/UPDATE数组或记录字段的部分操作中，例如
 *			UPDATE table SET foo[2] = 42, foo[4] = 43;
 * 我们可以将这种操作合并为单个赋值操作。本质上，我们想要在这种情况下产生的表达式是
 *		foo = array_set_element(array_set_element(foo, 2, 42), 4, 43)
 *
 * 3. 将目标列表按标准顺序排序：非无效字段按resno顺序排列，然后是无效字段（这些字段的顺序无关紧要）。
 *
 * 我们必须在触发重写规则之前完成第1和第2项，否则对NEW.foo的重写引用将产生错误或不完整的结果。第3项在重写时不是必需的，但对于计划者有帮助，我们在处理其他项目时可以基本上免费完成它。
 *
 * 如果values_rte非NULL（即我们正在使用来自VALUES RTE的值进行多行INSERT），我们将*unused_values_attrnos填充为来自VALUES RTE的任何未使用列的属性编号。这可能发生在目标列表条目被生成表达式替换的身份列和生成列中（如果使用INSERT ... OVERRIDING USER VALUE，或者所有要插入的值为DEFAULT）。重写函数rewriteValuesRTE()需要此信息以处理未使用列中的任何DEFAULT项目。调用者必须将*unused_values_attrnos初始化为NULL。
 */
static List * fc_rewriteTargetListIU(List *fc_targetList,
					CmdType fc_commandType,
					OverridingKind fc_override,
					Relation fc_target_relation,
					RangeTblEntry *fc_values_rte,
					int fc_values_rte_index,
					Bitmapset **fc_unused_values_attrnos)
{
	TargetEntry **fc_new_tles;
	List	   *fc_new_tlist = NIL;
	List	   *fc_junk_tlist = NIL;
	Form_pg_attribute fc_att_tup;
	int			fc_attrno,
				fc_next_junk_attrno,
				fc_numattrs;
	ListCell   *fc_temp;
	Bitmapset  *fc_default_only_cols = NULL;

	/*
	 * 我们通过扫描输入目标列表一次并将TLE转移到数组中，然后扫描该数组以构建输出目标列表来处理正常（非无效）属性。这样可以避免大量属性时的O(N^2)行为。
	 *
	 * 在同一目标列表扫描期间，无效属性被抛入单独的列表，然后附加到重构的目标列表中。
	 */
	fc_numattrs = RelationGetNumberOfAttributes(fc_target_relation);
	fc_new_tles = (TargetEntry **) palloc0(fc_numattrs * sizeof(TargetEntry *));
	fc_next_junk_attrno = fc_numattrs + 1;

	foreach(fc_temp, fc_targetList)
	{
		TargetEntry *fc_old_tle = (TargetEntry *) lfirst(fc_temp);

		if (!fc_old_tle->resjunk)
		{
			/* 正常属性：将其存储到new_tles[]中 */
			fc_attrno = fc_old_tle->resno;
			if (fc_attrno < 1 || fc_attrno > fc_numattrs)
				elog(ERROR, "bogus resno %d in targetlist", fc_attrno);
			fc_att_tup = TupleDescAttr(fc_target_relation->rd_att, fc_attrno - 1);

			/* 我们可以（并且必须）忽略已删除的属性 */
			if (fc_att_tup->attisdropped)
				continue;

			/* 与同一属性的任何先前赋值合并 */
			fc_new_tles[fc_attrno - 1] =
				fc_process_matched_tle(fc_old_tle,
									fc_new_tles[fc_attrno - 1],
									NameStr(fc_att_tup->attname));
		}
		else
		{
			/*
			 * 将所有resjunk目标列表条目复制到junk_tlist，并将它们分配给最后一个实际resno以上的resnos。
			 *
			 * 典型的无效条目包括ORDER BY或GROUP BY表达式（在INSERT或UPDATE中实际上可能存在这些吗？），系统属性引用等。
			 */

			/* 正确获取 resno，但不要不必要地复制 */
			if (fc_old_tle->resno != fc_next_junk_attrno)
			{
				fc_old_tle = flatCopyTargetEntry(fc_old_tle);
				fc_old_tle->resno = fc_next_junk_attrno;
			}
			fc_junk_tlist = lappend(fc_junk_tlist, fc_old_tle);
			fc_next_junk_attrno++;
		}
	}

	for (fc_attrno = 1; fc_attrno <= fc_numattrs; fc_attrno++)
	{
		TargetEntry *fc_new_tle = fc_new_tles[fc_attrno - 1];
		bool		fc_apply_default;

		fc_att_tup = TupleDescAttr(fc_target_relation->rd_att, fc_attrno - 1);

		/* 我们可以（并且必须）忽略已删除的属性 */
		if (fc_att_tup->attisdropped)
			continue;

		/*
		 * 处理两个需要插入默认表达式的情况：要么是INSERT且目标列表中没有该列的条目，要么目标列表条目是DEFAULT占位节点。
		 */
		fc_apply_default = ((fc_new_tle == NULL && fc_commandType == CMD_INSERT) ||
						 (fc_new_tle && fc_new_tle->expr && IsA(fc_new_tle->expr, SetToDefault)));

		if (fc_commandType == CMD_INSERT)
		{
			int			fc_values_attrno = 0;

			/* 来自VALUES RTE的值的源属性编号 */
			if (fc_values_rte && fc_new_tle && IsA(fc_new_tle->expr, Var))
			{
				Var		   *fc_var = (Var *) fc_new_tle->expr;

				if (fc_var->varno == fc_values_rte_index)
					fc_values_attrno = fc_var->varattno;
			}

			/*
			 * 只能将DEFAULT插入到GENERATED ALWAYS身份列中，除非指定了OVERRIDING USER VALUE或OVERRIDING SYSTEM VALUE。
			 */
			if (fc_att_tup->attidentity == ATTRIBUTE_IDENTITY_ALWAYS && !fc_apply_default)
			{
				if (fc_override == OVERRIDING_USER_VALUE)
					fc_apply_default = true;
				else if (fc_override != OVERRIDING_SYSTEM_VALUE)
				{
					/*
					 * 如果该列的值来自VALUES RTE，请测试它是否只包含SetToDefault项目。由于VALUES列表可能相当大，我们安排只扫描一次。
					 */
					if (fc_values_attrno != 0)
					{
						if (fc_default_only_cols == NULL)
							fc_default_only_cols = fc_findDefaultOnlyColumns(fc_values_rte);

						if (bms_is_member(fc_values_attrno, fc_default_only_cols))
							fc_apply_default = true;
					}

					if (!fc_apply_default)
						ereport(ERROR,
								(errcode(ERRCODE_GENERATED_ALWAYS),
								 errmsg("cannot insert a non-DEFAULT value into column \"%s\"",
										NameStr(fc_att_tup->attname)),
								 errdetail("Column \"%s\" is an identity column defined as GENERATED ALWAYS.",
										   NameStr(fc_att_tup->attname)),
								 errhint("Use OVERRIDING SYSTEM VALUE to override.")));
				}
			}

			/*
			 * 虽然允许插入到GENERATED BY DEFAULT身份列中，但如果指定了OVERRIDING USER VALUE，则应应用默认值。
			 */
			if (fc_att_tup->attidentity == ATTRIBUTE_IDENTITY_BY_DEFAULT &&
				fc_override == OVERRIDING_USER_VALUE)
				fc_apply_default = true;

			/*
			 * 只能将DEFAULT插入到生成列，无论是否存在任何OVERRIDING子句。
			 */
			if (fc_att_tup->attgenerated && !fc_apply_default)
			{
				/*
				 * 如果该列的值来自VALUES RTE，请测试它是否只包含SetToDefault项目，如上所述。
				 */
				if (fc_values_attrno != 0)
				{
					if (fc_default_only_cols == NULL)
						fc_default_only_cols = fc_findDefaultOnlyColumns(fc_values_rte);

					if (bms_is_member(fc_values_attrno, fc_default_only_cols))
						fc_apply_default = true;
				}

				if (!fc_apply_default)
					ereport(ERROR,
							(errcode(ERRCODE_GENERATED_ALWAYS),
							 errmsg("cannot insert a non-DEFAULT value into column \"%s\"",
									NameStr(fc_att_tup->attname)),
							 errdetail("Column \"%s\" is a generated column.",
									   NameStr(fc_att_tup->attname))));
			}

			/*
			 * 对于从VALUES RTE的INSERT，返回任何不再使用的VALUES列的属性编号（由于目标列表条目被默认表达式替换）。
			 */
			if (fc_values_attrno != 0 && fc_apply_default && fc_unused_values_attrnos)
				*fc_unused_values_attrnos = bms_add_member(*fc_unused_values_attrnos,
														fc_values_attrno);
		}

		/*
		 * 对身份列和生成列的更新遵循与上述相同的规则，除了UPDATE不允许OVERRIDING子句。此外，源不能是VALUES RTE，因此我们无需考虑此点。
		 */
		if (fc_commandType == CMD_UPDATE)
		{
			if (fc_att_tup->attidentity == ATTRIBUTE_IDENTITY_ALWAYS &&
				fc_new_tle && !fc_apply_default)
				ereport(ERROR,
						(errcode(ERRCODE_GENERATED_ALWAYS),
						 errmsg("column \"%s\" can only be updated to DEFAULT",
								NameStr(fc_att_tup->attname)),
						 errdetail("Column \"%s\" is an identity column defined as GENERATED ALWAYS.",
								   NameStr(fc_att_tup->attname))));

			if (fc_att_tup->attgenerated && fc_new_tle && !fc_apply_default)
				ereport(ERROR,
						(errcode(ERRCODE_GENERATED_ALWAYS),
						 errmsg("column \"%s\" can only be updated to DEFAULT",
								NameStr(fc_att_tup->attname)),
						 errdetail("Column \"%s\" is a generated column.",
								   NameStr(fc_att_tup->attname))));
		}

		if (fc_att_tup->attgenerated)
		{
			/*
			 * 存储的生成列将在执行者中修正
			 */
			fc_new_tle = NULL;
		}
		else if (fc_apply_default)
		{
			Node	   *fc_new_expr;

			fc_new_expr = build_column_default(fc_target_relation, fc_attrno);

			/*
			 * 如果没有默认（即，默认实际上为NULL），我们可以在INSERT情况下省略目标列表条目，因为计划者可以自己插入NULL，并且在该条目上花费更多重写器周期没有意义。但在UPDATE情况下，我们必须显式将列设置为NULL。
			 */
			if (!fc_new_expr)
			{
				if (fc_commandType == CMD_INSERT)
					fc_new_tle = NULL;
				else
					fc_new_expr = coerce_null_to_domain(fc_att_tup->atttypid,
													 fc_att_tup->atttypmod,
													 fc_att_tup->attcollation,
													 fc_att_tup->attlen,
													 fc_att_tup->attbyval);
			}

			if (fc_new_expr)
				fc_new_tle = makeTargetEntry((Expr *) fc_new_expr,
										  fc_attrno,
										  pstrdup(NameStr(fc_att_tup->attname)),
										  false);
		}

		if (fc_new_tle)
			fc_new_tlist = lappend(fc_new_tlist, fc_new_tle);
	}

	pfree(fc_new_tles);

	return list_concat(fc_new_tlist, fc_junk_tlist);
}



/*
 * 将原始tlist中的匹配TLE转换为正确的新TLE。
 *
 * 此例程检测并处理对同一目标属性的多个赋值。
 * （属性名称仅在错误消息中需要。）
 */
static TargetEntry * fc_process_matched_tle(TargetEntry *fc_src_tle,
					TargetEntry *fc_prior_tle,
					const char *fc_attrName)
{
	TargetEntry *fc_result;
	CoerceToDomain *fc_coerce_expr = NULL;
	Node	   *fc_src_expr;
	Node	   *fc_prior_expr;
	Node	   *fc_src_input;
	Node	   *fc_prior_input;
	Node	   *fc_priorbottom;
	Node	   *fc_newexpr;

	if (fc_prior_tle == NULL)
	{
		/*
		 * 正常情况下，这是对该属性的第一次赋值。
		 */
		return fc_src_tle;
	}

	/*----------
	 * 对同一属性的多重赋值。 仅在所有操作都是
	 * FieldStore 或 SubscriptingRef 赋值操作时允许。 这有点
	 * 棘手，因为我们实际上可能正在查看的是一系列
	 * 这样的节点；考虑
	 *		UPDATE tab SET col.fld1.subfld1 = x, col.fld2.subfld2 = y
	 * 解析器产生的两个表达式将看起来像
	 *		FieldStore(col, fld1, FieldStore(placeholder, subfld1, x))
	 *		FieldStore(col, fld2, FieldStore(placeholder, subfld2, y))
	 * 然而，我们可以忽略子结构，仅考虑每个赋值的顶层
	 * FieldStore 或 SubscriptingRef，因为它可以组合成
	 *		FieldStore(FieldStore(col, fld1,
	 *							  FieldStore(placeholder, subfld1, x)),
	 *				   fld2, FieldStore(placeholder, subfld2, y))
	 * 请注意最左侧的表达式放在内部，这样赋值看起来是从左到右发生的。
	 *
	 * 对于 FieldStore，我们可以生成一个包含多个目标字段的单个
	 * FieldStore，而不是嵌套。 但是当涉及到 SubscriptingRefs 时，我们必须嵌套。
	 *
	 * 更复杂的是，目标列可能是一个域，
	 * 这导致每个赋值包含一个 CoerceToDomain 节点，
	 * 其上是 FieldStore 或 SubscriptingRef。 这些应该具有匹配的目标
	 * 域，因此我们将它们剥离，然后在
	 * 组合的 FieldStore/SubscriptingRef 节点上重构一个单一的 CoerceToDomain。  (注意，这导致域的检查仅在完成所有
	 * 字段或元素更新后才应用，而不是在每次操作后。 这是可取的。)
	 *----------
	 */
	fc_src_expr = (Node *) fc_src_tle->expr;
	fc_prior_expr = (Node *) fc_prior_tle->expr;

	if (fc_src_expr && IsA(fc_src_expr, CoerceToDomain) &&
		fc_prior_expr && IsA(fc_prior_expr, CoerceToDomain) &&
		((CoerceToDomain *) fc_src_expr)->resulttype ==
		((CoerceToDomain *) fc_prior_expr)->resulttype)
	{
		/* 我们假设不检查 resulttypmod/resultcollid 匹配 */
		fc_coerce_expr = (CoerceToDomain *) fc_src_expr;
		fc_src_expr = (Node *) ((CoerceToDomain *) fc_src_expr)->arg;
		fc_prior_expr = (Node *) ((CoerceToDomain *) fc_prior_expr)->arg;
	}

	fc_src_input = get_assignment_input(fc_src_expr);
	fc_prior_input = get_assignment_input(fc_prior_expr);
	if (fc_src_input == NULL ||
		fc_prior_input == NULL ||
		exprType(fc_src_expr) != exprType(fc_prior_expr))
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("multiple assignments to same column \"%s\"",
						fc_attrName)));

	/*
	 * 如果我们执行多次，这个 TLE 可能是赋值的嵌套。
	 */
	fc_priorbottom = fc_prior_input;
	for (;;)
	{
		Node	   *fc_newbottom = get_assignment_input(fc_priorbottom);

		if (fc_newbottom == NULL)
			break;				/* 找到原始 Var 引用 */
		fc_priorbottom = fc_newbottom;
	}
	if (!equal(fc_priorbottom, fc_src_input))
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("multiple assignments to same column \"%s\"",
						fc_attrName)));

	/*
	 * 看来嵌套它们是可以的。
	 */
	if (IsA(fc_src_expr, FieldStore))
	{
		FieldStore *fc_fstore = makeNode(FieldStore);

		if (IsA(fc_prior_expr, FieldStore))
		{
			/* 合并这两个 */
			memcpy(fc_fstore, fc_prior_expr, sizeof(FieldStore));
			fc_fstore->newvals =
				list_concat_copy(((FieldStore *) fc_prior_expr)->newvals,
								 ((FieldStore *) fc_src_expr)->newvals);
			fc_fstore->fieldnums =
				list_concat_copy(((FieldStore *) fc_prior_expr)->fieldnums,
								 ((FieldStore *) fc_src_expr)->fieldnums);
		}
		else
		{
			/* 一般情况，直接嵌套它们 */
			memcpy(fc_fstore, fc_src_expr, sizeof(FieldStore));
			fc_fstore->arg = (Expr *) fc_prior_expr;
		}
		fc_newexpr = (Node *) fc_fstore;
	}
	else if (IsA(fc_src_expr, SubscriptingRef))
	{
		SubscriptingRef *fc_sbsref = makeNode(SubscriptingRef);

		memcpy(fc_sbsref, fc_src_expr, sizeof(SubscriptingRef));
		fc_sbsref->refexpr = (Expr *) fc_prior_expr;
		fc_newexpr = (Node *) fc_sbsref;
	}
	else
	{
		elog(ERROR, "cannot happen");
		fc_newexpr = NULL;
	}

	if (fc_coerce_expr)
	{
		/* 恢复 CoerceToDomain */
		CoerceToDomain *fc_newcoerce = makeNode(CoerceToDomain);

		memcpy(fc_newcoerce, fc_coerce_expr, sizeof(CoerceToDomain));
		fc_newcoerce->arg = (Expr *) fc_newexpr;
		fc_newexpr = (Node *) fc_newcoerce;
	}

	fc_result = flatCopyTargetEntry(fc_src_tle);
	fc_result->expr = (Expr *) fc_newexpr;
	return fc_result;
}

/*
 * 如果节点是赋值节点，返回其输入；否则返回 NULL
 */
static Node * get_assignment_input(Node *fc_node)
{
	if (fc_node == NULL)
		return NULL;
	if (IsA(fc_node, FieldStore))
	{
		FieldStore *fc_fstore = (FieldStore *) fc_node;

		return (Node *) fc_fstore->arg;
	}
	else if (IsA(fc_node, SubscriptingRef))
	{
		SubscriptingRef *fc_sbsref = (SubscriptingRef *) fc_node;

		if (fc_sbsref->refassgnexpr == NULL)
			return NULL;

		return (Node *) fc_sbsref->refexpr;
	}

	return NULL;
}

/*
 * 为列的默认值创建一个表达式树。
 *
 * 如果没有默认值，则返回 NULL。
 */
Node * build_column_default(Relation fc_rel, int fc_attrno)
{
	TupleDesc	fc_rd_att = fc_rel->rd_att;
	Form_pg_attribute fc_att_tup = TupleDescAttr(fc_rd_att, fc_attrno - 1);
	Oid			fc_atttype = fc_att_tup->atttypid;
	int32		fc_atttypmod = fc_att_tup->atttypmod;
	Node	   *fc_expr = NULL;
	Oid			fc_exprtype;

	if (fc_att_tup->attidentity)
	{
		NextValueExpr *fc_nve = makeNode(NextValueExpr);

		fc_nve->seqid = getIdentitySequence(RelationGetRelid(fc_rel), fc_attrno, false);
		fc_nve->typeId = fc_att_tup->atttypid;

		return (Node *) fc_nve;
	}

	/*
	 * 如果关系为此列具有默认值，获取该表达式。
	 */
	if (fc_att_tup->atthasdef)
	{
		if (fc_rd_att->constr && fc_rd_att->constr->num_defval > 0)
		{
			AttrDefault *fc_defval = fc_rd_att->constr->defval;
			int			fc_ndef = fc_rd_att->constr->num_defval;

			while (--fc_ndef >= 0)
			{
				if (fc_attrno == fc_defval[fc_ndef].adnum)
				{
					/* 找到了，将字符串表示转换为节点树。 */
					fc_expr = stringToNode(fc_defval[fc_ndef].adbin);
					break;
				}
			}
		}
		if (fc_expr == NULL)
			elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
				 fc_attrno, RelationGetRelationName(fc_rel));
	}

	/*
	 * 没有每列的默认值，因此查找类型本身的默认值。 但是
	 * 不适用于生成的列。
	 */
	if (fc_expr == NULL && !fc_att_tup->attgenerated)
		fc_expr = get_typdefault(fc_atttype);

	if (fc_expr == NULL)
		return NULL;			/* 到处都没有默认值 */

	/*
	 * 确保值被强制转换为目标列类型；这通常应该是
	 * 真实的，但似乎有一些涉及域默认值的特殊情况
	 * 可能不真实。 这应该与解析器对非默认表达式的处理匹配 --- 见
	 * transformAssignedExpr()。
	 */
	fc_exprtype = exprType(fc_expr);

	fc_expr = coerce_to_target_type(NULL,	/* 此处没有 UNKNOWN 参数 */
								 fc_expr, fc_exprtype,
								 fc_atttype, fc_atttypmod,
								 COERCION_ASSIGNMENT,
								 COERCE_IMPLICIT_CAST,
								 -1);
	if (fc_expr == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
				 errmsg("column \"%s\" is of type %s"
						" but default expression is of type %s",
						NameStr(fc_att_tup->attname),
						format_type_be(fc_atttype),
						format_type_be(fc_exprtype)),
				 errhint("You will need to rewrite or cast the expression.")));

	return fc_expr;
}


/* VALUES RTE 是否包含任何 SetToDefault 项目？ */
static bool fc_searchForDefault(RangeTblEntry *fc_rte)
{
	ListCell   *fc_lc;

	foreach(fc_lc, fc_rte->values_lists)
	{
		List	   *fc_sublist = (List *) lfirst(fc_lc);
		ListCell   *fc_lc2;

		foreach(fc_lc2, fc_sublist)
		{
			Node	   *fc_col = (Node *) lfirst(fc_lc2);

			if (IsA(fc_col, SetToDefault))
				return true;
		}
	}
	return false;
}


/*
 * 在 VALUES RTE 中搜索仅包含 SetToDefault 项目的列，
 * 返回一个包含任何此类列的属性编号的 Bitmapset。
 */
static Bitmapset * fc_findDefaultOnlyColumns(RangeTblEntry *fc_rte)
{
	Bitmapset  *fc_default_only_cols = NULL;
	ListCell   *fc_lc;

	foreach(fc_lc, fc_rte->values_lists)
	{
		List	   *fc_sublist = (List *) lfirst(fc_lc);
		ListCell   *fc_lc2;
		int			fc_i;

		if (fc_default_only_cols == NULL)
		{
			/* 从第一行填充初始结果位图 */
			fc_i = 0;
			foreach(fc_lc2, fc_sublist)
			{
				Node	   *fc_col = (Node *) lfirst(fc_lc2);

				fc_i++;
				if (IsA(fc_col, SetToDefault))
					fc_default_only_cols = bms_add_member(fc_default_only_cols, fc_i);
			}
		}
		else
		{
			/* 从下一行更新结果位图 */
			fc_i = 0;
			foreach(fc_lc2, fc_sublist)
			{
				Node	   *fc_col = (Node *) lfirst(fc_lc2);

				fc_i++;
				if (!IsA(fc_col, SetToDefault))
					fc_default_only_cols = bms_del_member(fc_default_only_cols, fc_i);
			}
		}

		/*
		 * 如果到目前为止读取的行中没有列仅包含 DEFAULT 项，
		 * 我们就完成了。
		 */
		if (bms_is_empty(fc_default_only_cols))
			break;
	}

	return fc_default_only_cols;
}



/*
 * 在处理带有VALUES RTE的INSERT ... VALUES（即，多个VALUES列表）时，我们必须用
 * 适当的默认表达式替换VALUES列表中的任何DEFAULT项。其他目标列表重写的方面
 * 仅需应用于查询的目标列表本身。
 *
 * 对于可自我更新的视图，VALUES列表中的每个DEFAULT项都将被视图的默认值替换（如果有的话）。
 * 否则，它将保持不变，以便稍后在重写查询以引用基础关系而不是视图时，可以应用
 * 基础关系的默认值。
 *
 * 对于其他类型的关系，包括规则和触发器可更新视图，所有DEFAULT项都会被替换，
 * 如果目标关系没有默认值，则会显式设置为NULL。
 *
 * 此外，如果在unused_cols中找到DEFAULT项，则会显式设置为NULL。这发生在VALUES RTE
 * 中的列，其对应的目标列表条目已经被替换为关系的默认表达式，因此这些列中的
 * 任何值不再使用。这种情况可能发生在身份和生成列（如果使用INSERT ... OVERRIDING
 * USER VALUE，或者所有要插入的值都是DEFAULT）。原则上，我们可以用NULL替换
 * 该列中的所有条目，无论是否为DEFAULT；但这似乎不值得费心。
 *
 * 注意，我们可能有下标或字段赋值的目标列表条目，以及来自已经替换的DEFAULT项的
 * 更复杂表达式，如果我们曾为一个可自我更新的视图递归到此。然而，除了上述
 * 未使用的列外，这种条目不应被赋予DEFAULT值 --- 我们只需为包含引用VALUES RTE的简单
 * Vars的目标列表条目，或不再被目标列表引用的条目替换DEFAULT项即可。
 *
 * 如果所有DEFAULT项都被替换，则返回true；如果有些未被触及，则返回false。
 */
static bool fc_rewriteValuesRTE(Query *fc_parsetree, RangeTblEntry *fc_rte, int fc_rti,
				 Relation fc_target_relation,
				 Bitmapset *fc_unused_cols)
{
	List	   *fc_newValues;
	ListCell   *fc_lc;
	bool		fc_isAutoUpdatableView;
	bool		fc_allReplaced;
	int			fc_numattrs;
	int		   *fc_attrnos;

	/* 下面的步骤对于非INSERT查询来说没有意义 */
	Assert(fc_parsetree->commandType == CMD_INSERT);
	Assert(fc_rte->rtekind == RTE_VALUES);

	/*
	 * 在一个大的VALUES列表中重建所有列表是一个相当昂贵的提议，如果
	 * 没有DEFAULT占位符，那就是浪费时间。所以首先扫描以查看是否存在。
	 */
	if (!fc_searchForDefault(fc_rte))
		return true;			/* 无需操作 */

	/*
	 * 扫描目标列表以查找引用VALUES RTE的条目，并记下目标属性。如上所述，
	 * 我们只需对包含简单Vars的目标列表条目执行此操作 --- VALUES RTE中的
	 * 其他内容不应包含DEFAULT项（除非可能是未使用的列），如果发生这种情况，
	 * 我们会抱怨。
	 */
	fc_numattrs = list_length(linitial(fc_rte->values_lists));
	fc_attrnos = (int *) palloc0(fc_numattrs * sizeof(int));

	foreach(fc_lc, fc_parsetree->targetList)
	{
		TargetEntry *fc_tle = (TargetEntry *) lfirst(fc_lc);

		if (IsA(fc_tle->expr, Var))
		{
			Var		   *fc_var = (Var *) fc_tle->expr;

			if (fc_var->varno == fc_rti)
			{
				int			fc_attrno = fc_var->varattno;

				Assert(fc_attrno >= 1 && fc_attrno <= fc_numattrs);
				fc_attrnos[fc_attrno - 1] = fc_tle->resno;
			}
		}
	}

	/*
	 * 检查目标关系是否为可自我更新的视图，在这种情况下未解决的默认值将
	 * 被保留而不是设置为NULL。
	 */
	fc_isAutoUpdatableView = false;
	if (fc_target_relation->rd_rel->relkind == RELKIND_VIEW &&
		!fc_view_has_instead_trigger(fc_target_relation, CMD_INSERT))
	{
		List	   *fc_locks;
		bool		fc_hasUpdate;
		bool		fc_found;
		ListCell   *fc_l;

		/* 寻找无条件的DO INSTEAD规则 */
		fc_locks = fc_matchLocks(CMD_INSERT, fc_target_relation->rd_rules,
						   fc_parsetree->resultRelation, fc_parsetree, &fc_hasUpdate);

		fc_found = false;
		foreach(fc_l, fc_locks)
		{
			RewriteRule *fc_rule_lock = (RewriteRule *) lfirst(fc_l);

			if (fc_rule_lock->isInstead &&
				fc_rule_lock->qual == NULL)
			{
				fc_found = true;
				break;
			}
		}

		/*
		 * 如果我们没有找到无条件的DO INSTEAD规则，则假定视图是可自我更新的。
		 * 如果不是，rewriteTargetView()将抛出错误。
		 */
		if (!fc_found)
			fc_isAutoUpdatableView = true;
	}

	fc_newValues = NIL;
	fc_allReplaced = true;
	foreach(fc_lc, fc_rte->values_lists)
	{
		List	   *fc_sublist = (List *) lfirst(fc_lc);
		List	   *fc_newList = NIL;
		ListCell   *fc_lc2;
		int			fc_i;

		Assert(list_length(fc_sublist) == fc_numattrs);

		fc_i = 0;
		foreach(fc_lc2, fc_sublist)
		{
			Node	   *fc_col = (Node *) lfirst(fc_lc2);
			int			fc_attrno = fc_attrnos[fc_i++];

			if (IsA(fc_col, SetToDefault))
			{
				Form_pg_attribute fc_att_tup;
				Node	   *fc_new_expr;

				/*
				 * 如果该列未被使用，只需将DEFAULT替换为NULL
				 * （在这种情况下，attrno将为0，因为目标列表条目
				 * 将已被替换为默认表达式）。
				 */
				if (bms_is_member(fc_i, fc_unused_cols))
				{
					SetToDefault *fc_def = (SetToDefault *) fc_col;

					fc_newList = lappend(fc_newList,
									  makeNullConst(fc_def->typeId,
													fc_def->typeMod,
													fc_def->collation));
					continue;
				}

				if (fc_attrno == 0)
					elog(ERROR, "cannot set value in column %d to DEFAULT", fc_i);
				Assert(fc_attrno > 0 && fc_attrno <= fc_target_relation->rd_att->natts);
				fc_att_tup = TupleDescAttr(fc_target_relation->rd_att, fc_attrno - 1);

				if (!fc_att_tup->attisdropped)
					fc_new_expr = build_column_default(fc_target_relation, fc_attrno);
				else
					fc_new_expr = NULL;	/* 如果被丢弃，则强制为NULL */

				/*
				 * 如果没有默认值（即，默认值实际上是NULL），
				 * 我们必须明确将列设置为NULL，除非目标关系是自动可更新视图。
				 */
				if (!fc_new_expr)
				{
					if (fc_isAutoUpdatableView)
					{
						/* 保持值不变 */
						fc_newList = lappend(fc_newList, fc_col);
						fc_allReplaced = false;
						continue;
					}

					fc_new_expr = coerce_null_to_domain(fc_att_tup->atttypid,
													 fc_att_tup->atttypmod,
													 fc_att_tup->attcollation,
													 fc_att_tup->attlen,
													 fc_att_tup->attbyval);
				}
				fc_newList = lappend(fc_newList, fc_new_expr);
			}
			else
				fc_newList = lappend(fc_newList, fc_col);
		}
		fc_newValues = lappend(fc_newValues, fc_newList);
	}
	fc_rte->values_lists = fc_newValues;

	pfree(fc_attrnos);

	return fc_allReplaced;
}

/*
 * 通过将给定VALUES RTE中的任何剩余DEFAULT项替换为NULL常量，
 * 来清理残余的DEFAULT项。
 *
 * 这用于由附加到自动可更新视图的DO ALSO规则生成的产品查询。
 * 操作不能依赖于“目标关系”，因为产品查询可能没有一个（不必是INSERT）。
 * 本质上，这样的查询被视为附加到规则可更新视图。
 */
static void fc_rewriteValuesRTEToNulls(Query *fc_parsetree, RangeTblEntry *fc_rte)
{
	List	   *fc_newValues;
	ListCell   *fc_lc;

	fc_newValues = NIL;
	foreach(fc_lc, fc_rte->values_lists)
	{
		List	   *fc_sublist = (List *) lfirst(fc_lc);
		List	   *fc_newList = NIL;
		ListCell   *fc_lc2;

		foreach(fc_lc2, fc_sublist)
		{
			Node	   *fc_col = (Node *) lfirst(fc_lc2);

			if (IsA(fc_col, SetToDefault))
			{
				SetToDefault *fc_def = (SetToDefault *) fc_col;

				fc_newList = lappend(fc_newList, makeNullConst(fc_def->typeId,
														 fc_def->typeMod,
														 fc_def->collation));
			}
			else
				fc_newList = lappend(fc_newList, fc_col);
		}
		fc_newValues = lappend(fc_newValues, fc_newList);
	}
	fc_rte->values_lists = fc_newValues;
}


/*
 * matchLocks -
 *	  匹配锁列表并返回匹配的规则
 */
static List * fc_matchLocks(CmdType fc_event,
		   RuleLock *fc_rulelocks,
		   int fc_varno,
		   Query *fc_parsetree,
		   bool *fc_hasUpdate)
{
	List	   *fc_matching_locks = NIL;
	int			fc_nlocks;
	int			fc_i;

	if (fc_rulelocks == NULL)
		return NIL;

	/* MERGE没有规则支持 */
	if (fc_parsetree->commandType == CMD_MERGE)
		return NIL;

	if (fc_parsetree->commandType != CMD_SELECT)
	{
		if (fc_parsetree->resultRelation != fc_varno)
			return NIL;
	}

	fc_nlocks = fc_rulelocks->numLocks;

	for (fc_i = 0; fc_i < fc_nlocks; fc_i++)
	{
		RewriteRule *fc_oneLock = fc_rulelocks->rules[fc_i];

		if (fc_oneLock->event == CMD_UPDATE)
			*fc_hasUpdate = true;

		/*
		 * 抑制在当前会话的复制角色下被禁用的ON INSERT/UPDATE/DELETE规则
		 * 或配置为不触发的规则。 ON SELECT规则将始终应用，以保持视图
		 * 即使在LOCAL或REPLICA角色下也能正常工作。
		 */
		if (fc_oneLock->event != CMD_SELECT)
		{
			if (SessionReplicationRole == SESSION_REPLICATION_ROLE_REPLICA)
			{
				if (fc_oneLock->enabled == RULE_FIRES_ON_ORIGIN ||
					fc_oneLock->enabled == RULE_DISABLED)
					continue;
			}
			else				/* ORIGIN或LOCAL ROLE */
			{
				if (fc_oneLock->enabled == RULE_FIRES_ON_REPLICA ||
					fc_oneLock->enabled == RULE_DISABLED)
					continue;
			}
		}

		if (fc_oneLock->event == fc_event)
		{
			if (fc_parsetree->commandType != CMD_SELECT ||
				rangeTableEntry_used((Node *) fc_parsetree, fc_varno, 0))
				fc_matching_locks = lappend(fc_matching_locks, fc_oneLock);
		}
	}

	return fc_matching_locks;
}


/*
 * ApplyRetrieveRule - 扩展ON SELECT规则
 */
static Query * fc_ApplyRetrieveRule(Query *fc_parsetree,
				  RewriteRule *fc_rule,
				  int fc_rt_index,
				  Relation fc_relation,
				  List *fc_activeRIRs)
{
	Query	   *fc_rule_action;
	RangeTblEntry *fc_rte,
			   *fc_subrte;
	RowMarkClause *fc_rc;
	int			fc_numCols;

	if (list_length(fc_rule->actions) != 1)
		elog(ERROR, "expected just one rule action");
	if (fc_rule->qual != NULL)
		elog(ERROR, "cannot handle qualified ON SELECT rule");

	/* 检查是否限制了非系统视图的扩展 */
	if (unlikely((restrict_nonsystem_relation_kind & RESTRICT_RELKIND_VIEW) != 0 &&
				 RelationGetRelid(fc_relation) >= FirstNormalObjectId))
		ereport(ERROR,
				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
				 errmsg("access to non-system view \"%s\" is restricted",
						RelationGetRelationName(fc_relation))));

	if (fc_rt_index == fc_parsetree->resultRelation)
	{
		/*
		 * 我们有一个作为查询结果关系的视图，并且它没有
		 * 被任何规则重写。 如果有INSTEAD OF触发器将捕获对插入/更新/删除
		 * 视图行的尝试，这种情况是支持的。 执行器会检查这一点；暂时继续。
		 * 我们有两种情况：
		 *
		 * 对于INSERT，我们无需做任何事情。 未修改的RTE将很好地作为
		 * 结果关系。
		 *
		 * 对于UPDATE/DELETE，我们需要扩展视图，以便为操作提供源
		 * 数据。 但我们还需要一个未修改的RTE作为目标。 所以，复制RTE并将
		 * 复制添加到rangetable。 请注意，复制不会被添加到jointree中。
		 * 还要注意，fireRIRrules中有一个小技巧，以避免在到达
		 * 复制的RTE时再次调用此函数。
		 */
		if (fc_parsetree->commandType == CMD_INSERT)
			return fc_parsetree;
		else if (fc_parsetree->commandType == CMD_UPDATE ||
				 fc_parsetree->commandType == CMD_DELETE)
		{
			RangeTblEntry *fc_newrte;
			Var		   *fc_var;
			TargetEntry *fc_tle;

			fc_rte = rt_fetch(fc_rt_index, fc_parsetree->rtable);
			fc_newrte = copyObject(fc_rte);
			fc_parsetree->rtable = lappend(fc_parsetree->rtable, fc_newrte);
			fc_parsetree->resultRelation = list_length(fc_parsetree->rtable);

			/*
			 * 不需要重复进行权限检查，因此清除
			 * 原始RTE的权限信息（我们更愿意保留
			 * 结果RTE上设置的位）。
			 */
			fc_rte->requiredPerms = 0;
			fc_rte->checkAsUser = InvalidOid;
			fc_rte->selectedCols = NULL;
			fc_rte->insertedCols = NULL;
			fc_rte->updatedCols = NULL;
			fc_rte->extraUpdatedCols = NULL;

			/*
			 * 在大多数情况下，引用视图的Vars应保持不变，
			 * 这意味着它们隐含地表示OLD值。
			 * 但在RETURNING列表中，如果有任何，我们希望这种Vars
			 * 表示NEW值，因此将它们更改为引用新的RTE。
			 *
			 * 由于ChangeVarNodes在树上就地修改，因此为了安全起见，
			 * 首先复制RETURNING列表。
			 */
			fc_parsetree->returningList = copyObject(fc_parsetree->returningList);
			ChangeVarNodes((Node *) fc_parsetree->returningList, fc_rt_index,
						   fc_parsetree->resultRelation, 0);

			/*
			 * 为了让执行器计算传递给INSTEAD OF触发器的原始视图行，
			 * 我们添加一个引用原始RTE的resjunk whole-row Var。
			 * 这将在稍后展开为计算视图行所有OLD值的RowExpr。
			 */
			fc_var = makeWholeRowVar(fc_rte, fc_rt_index, 0, false);
			fc_tle = makeTargetEntry((Expr *) fc_var,
								  list_length(fc_parsetree->targetList) + 1,
								  pstrdup("wholerow"),
								  true);

			fc_parsetree->targetList = lappend(fc_parsetree->targetList, fc_tle);

			/* 现在，继续扩展原始视图RTE */
		}
		else
			elog(ERROR, "unrecognized commandType: %d",
				 (int) fc_parsetree->commandType);
	}

	/*
	 * 检查是否有FOR [KEY] UPDATE/SHARE子句适用于该视图。
	 *
	 * 注意：我们不需要明确考虑出现在
	 * 祖先查询级别中的任何此类子句；它们的影响已被markQueryForLocking
	 * 推送到这里，并将在“rc”中反映。
	 */
	fc_rc = get_parse_rowmark(fc_parsetree, fc_rt_index);

	/*
	 * 创建视图查询的可修改副本，并在提到的关系上获取所需的锁定。
	 * 如果有影响该视图的FOR [KEY] UPDATE/SHARE子句，强制对所有这些
	 * 关系至少获取RowShareLock。
	 */
	fc_rule_action = copyObject(linitial(fc_rule->actions));

	AcquireRewriteLocks(fc_rule_action, true, (fc_rc != NULL));

	/*
	 * 如果视图的FOR [KEY] UPDATE/SHARE，标记所有包含的表为
	 * 隐式FOR [KEY] UPDATE/SHARE，就像解析器会在
	 * 如果将视图的子查询显式编写时一样。
	 */
	if (fc_rc != NULL)
		fc_markQueryForLocking(fc_rule_action, (Node *) fc_rule_action->jointree,
							fc_rc->strength, fc_rc->waitPolicy, true);

	/*
	 * 递归扩展视图内的任何视图引用。
	 *
	 * 注意：这必须发生在markQueryForLocking之后。
	 * 这样，任何子视图所需的UPDATE权限位将通过markQueryForLocking
	 * 初始应用于它们的RTE_RELATION RTEs，然后通过下面的操作
	 * （此例程的递归调用）转移到它们的OLD rangetable条目。
	 */
	fc_rule_action = fc_fireRIRrules(fc_rule_action, fc_activeRIRs);

	/*
	 * 如果视图查询确实具有行安全性，请确保将查询标记为具备行安全性。
	 */
	fc_parsetree->hasRowSecurity |= fc_rule_action->hasRowSecurity;

	/*
	 * 现在，将视图查询插入为子选择，将关系的原始
	 * RTE转换为子查询RTE。
	 */
	fc_rte = rt_fetch(fc_rt_index, fc_parsetree->rtable);

	fc_rte->rtekind = RTE_SUBQUERY;
	fc_rte->subquery = fc_rule_action;
	fc_rte->security_barrier = RelationIsSecurityView(fc_relation);
	/* 清除在子查询 RTE 中不应设置的字段 */
	fc_rte->relid = InvalidOid;
	fc_rte->relkind = 0;
	fc_rte->rellockmode = 0;
	fc_rte->tablesample = NULL;
	fc_rte->inh = false;			/* 不得为子查询设置 */

	/*
	 * 我们将视图的权限检查数据移动到其rangetable中。
	 * 检查实际上将在旧条目上执行。
	 */
	fc_subrte = rt_fetch(PRS2_OLD_VARNO, fc_rule_action->rtable);
	Assert(fc_subrte->relid == fc_relation->rd_id);
	fc_subrte->requiredPerms = fc_rte->requiredPerms;
	fc_subrte->checkAsUser = fc_rte->checkAsUser;
	fc_subrte->selectedCols = fc_rte->selectedCols;
	fc_subrte->insertedCols = fc_rte->insertedCols;
	fc_subrte->updatedCols = fc_rte->updatedCols;
	fc_subrte->extraUpdatedCols = fc_rte->extraUpdatedCols;

	fc_rte->requiredPerms = 0;		/* 不对子查询本身进行权限检查 */
	fc_rte->checkAsUser = InvalidOid;
	fc_rte->selectedCols = NULL;
	fc_rte->insertedCols = NULL;
	fc_rte->updatedCols = NULL;
	fc_rte->extraUpdatedCols = NULL;

	
/*
	 * 由于我们允许 CREATE OR REPLACE VIEW 向视图添加列，所以在解析当前查询时，
	 * rule_action 可能会发出比我们预期更多的列。各种地方预计 rte->eref->colnames
	 * 与子查询的非垃圾输出列保持一致，因此在必要时通过添加一些虚拟列名来修正。
	 */
	fc_numCols = ExecCleanTargetListLength(fc_rule_action->targetList);
	while (list_length(fc_rte->eref->colnames) < fc_numCols)
	{
		fc_rte->eref->colnames = lappend(fc_rte->eref->colnames,
									  makeString(pstrdup("?column?")));
	}

	return fc_parsetree;
}

/*
 * 递归标记视图使用的所有关系为 FOR [KEY] UPDATE/SHARE。
 *
 * 这可能会生成一个无效查询，例如，如果某些子查询使用了聚合。我们将其留给规划器进行检测。
 *
 * 注意：这必须与解析器的 transformLockingClause() 例程一致。
 * 然而，与解析器不同，我们必须小心，不要标记视图的 OLD 和 NEW 关系以进行更新。
 * 处理这种情况的最好方法似乎是扫描联合树以确定使用了哪些关系。
 */
static void fc_markQueryForLocking(Query *fc_qry, Node *fc_jtnode,
					LockClauseStrength fc_strength, LockWaitPolicy fc_waitPolicy,
					bool fc_pushedDown)
{
	if (fc_jtnode == NULL)
		return;
	if (IsA(fc_jtnode, RangeTblRef))
	{
		int			fc_rti = ((RangeTblRef *) fc_jtnode)->rtindex;
		RangeTblEntry *fc_rte = rt_fetch(fc_rti, fc_qry->rtable);

		if (fc_rte->rtekind == RTE_RELATION)
		{
			applyLockingClause(fc_qry, fc_rti, fc_strength, fc_waitPolicy, fc_pushedDown);
			fc_rte->requiredPerms |= ACL_SELECT_FOR_UPDATE;
		}
		else if (fc_rte->rtekind == RTE_SUBQUERY)
		{
			applyLockingClause(fc_qry, fc_rti, fc_strength, fc_waitPolicy, fc_pushedDown);
			/* FOR UPDATE/SHARE 子查询传播到子查询的关系 */
			fc_markQueryForLocking(fc_rte->subquery, (Node *) fc_rte->subquery->jointree,
								fc_strength, fc_waitPolicy, true);
		}
		/* 其他 RTE 类型不受 FOR UPDATE 影响 */
	}
	else if (IsA(fc_jtnode, FromExpr))
	{
		FromExpr   *fc_f = (FromExpr *) fc_jtnode;
		ListCell   *fc_l;

		foreach(fc_l, fc_f->fromlist)
			fc_markQueryForLocking(fc_qry, lfirst(fc_l), fc_strength, fc_waitPolicy, fc_pushedDown);
	}
	else if (IsA(fc_jtnode, JoinExpr))
	{
		JoinExpr   *fc_j = (JoinExpr *) fc_jtnode;

		fc_markQueryForLocking(fc_qry, fc_j->larg, fc_strength, fc_waitPolicy, fc_pushedDown);
		fc_markQueryForLocking(fc_qry, fc_j->rarg, fc_strength, fc_waitPolicy, fc_pushedDown);
	}
	else
		elog(ERROR, "unrecognized node type: %d",
			 (int) nodeTag(fc_jtnode));
}


/*
 * fireRIRonSubLink -
 *	将 fireRIRrules() 应用到给定树中找到的每个 SubLink（表达式中的子选择）。
 *
 * 注意：尽管这具有 walker 的形式，但我们采用了变通方法并就地修改了 SubLink 节点。
 * 确保不发生任何不必要的副作用是调用者的责任！
 *
 * 这与大多数递归进入子选择的其他例程不同，因为我们必须在 SubLink 节点控制
 * 以将 SubLink 的子选择链接替换为可能已重写的子查询。
 */
static bool fc_fireRIRonSubLink(Node *fc_node, fireRIRonSubLink_context *fc_context)
{
	if (fc_node == NULL)
		return false;
	if (IsA(fc_node, SubLink))
	{
		SubLink    *fc_sub = (SubLink *) fc_node;

		/* 做我们来这里的目的 */
		fc_sub->subselect = (Node *) fc_fireRIRrules((Query *) fc_sub->subselect,
											   fc_context->activeRIRs);

		/*
		 * 记住是否有任何子链接具有行安全性。
		 */
		fc_context->hasRowSecurity |= ((Query *) fc_sub->subselect)->hasRowSecurity;

		/* 继续处理 SubLink 的左参数 */
	}

	/*
	 * 不要递归进入 Query 节点，因为 fireRIRrules 已经为我们处理了子选择的子选择。
	 */
	return expression_tree_walker(fc_node, fc_fireRIRonSubLink,
								  (void *) fc_context);
}


/*
 * fireRIRrules -
 *	应用所有 RIR 规则于给定查询中的每个 rangetable 条目
 *
 * activeRIRs 是我们已经在处理 RIR 规则的视图 OID 列表，用于检测/拒绝递归。
 */
static Query * fc_fireRIRrules(Query *fc_parsetree, List *fc_activeRIRs)
{
	int			fc_origResultRelation = fc_parsetree->resultRelation;
	int			fc_rt_index;
	ListCell   *fc_lc;

	/*
	 * 扩展 CTE 中的 SEARCH 和 CYCLE 子句。
	 *
	 * 这里只是一个方便的地方来执行此操作，因为我们已经
	 * 在查看每个查询。
	 */
	foreach(fc_lc, fc_parsetree->cteList)
	{
		CommonTableExpr *fc_cte = lfirst_node(CommonTableExpr, fc_lc);

		if (fc_cte->search_clause || fc_cte->cycle_clause)
		{
			fc_cte = rewriteSearchAndCycle(fc_cte);
			lfirst(fc_lc) = fc_cte;
		}
	}

	/*
	 * 不要尝试将其转换为 foreach 循环，因为 rtable 列表可以
	 * 在每次循环中改变...
	 */
	fc_rt_index = 0;
	while (fc_rt_index < list_length(fc_parsetree->rtable))
	{
		RangeTblEntry *fc_rte;
		Relation	fc_rel;
		List	   *fc_locks;
		RuleLock   *fc_rules;
		RewriteRule *fc_rule;
		int			fc_i;

		++fc_rt_index;

		fc_rte = rt_fetch(fc_rt_index, fc_parsetree->rtable);

		/*
		 * 子查询 RTE 不能有关联规则，因此在查询的这个层面上没有
		 * 需要做的事情，但我们必须递归进入子查询以扩展其中的
		 * 任何规则引用。
		 */
		if (fc_rte->rtekind == RTE_SUBQUERY)
		{
			fc_rte->subquery = fc_fireRIRrules(fc_rte->subquery, fc_activeRIRs);

			/*
			 * 在这里时，确保如果其任何子查询有行
			 * 安全性，则将查询标记为具有行安全性。
			 */
			fc_parsetree->hasRowSecurity |= fc_rte->subquery->hasRowSecurity;

			continue;
		}

		/*
		 * 连接和其他非关系 RTE 可以完全忽略。
		 */
		if (fc_rte->rtekind != RTE_RELATION)
			continue;

		/*
		 * 始终忽略在查询中引用的物化视图的 RIR 规则。
		 * （这不防止刷新 MV，因为它们在自己的查询定义中未被引用。）
		 *
		 * 注意：未来我们可能希望允许 MV 条件性地
		 * 扩展，就像它们是常规视图一样，如果它们不可扫描。
		 * 在这种情况下，这个测试需要推迟到我们打开关系之后，
		 * 以便检查其状态。
		 */
		if (fc_rte->relkind == RELKIND_MATVIEW)
			continue;

		/*
		 * 在 INSERT ... ON CONFLICT 中，忽略 EXCLUDED 伪关系；
		 * 即使它指向一个视图，我们也不需要扩展它，并且不应该
		 * 扩展，因为我们希望 RTE 保持为 RTE_RELATION 类型。
		 * 否则，它将被更改为 RTE_SUBQUERY 类型，这是一个
		 * 未经测试/不支持的情况。
		 */
		if (fc_parsetree->onConflict &&
			fc_rt_index == fc_parsetree->onConflict->exclRelIndex)
			continue;

		/*
		 * 如果表在查询中未被引用，则我们忽略它。
		 * 这可以防止由于规则扩展插入的新 rtable 条目而导致的
		 * 无限扩展循环。如果表被引用，则它是连接集的一部分
		 * （源表），或被任何 Var 节点引用，或是结果表。
		 */
		if (fc_rt_index != fc_parsetree->resultRelation &&
			!rangeTableEntry_used((Node *) fc_parsetree, fc_rt_index, 0))
			continue;

		/*
		 * 此外，如果这是由 ApplyRetrieveRule 引入的新结果关系，
		 * 我们不想进一步处理它。
		 */
		if (fc_rt_index == fc_parsetree->resultRelation &&
			fc_rt_index != fc_origResultRelation)
			continue;

		/*
		 * 由于解析器或 AcquireRewriteLocks 应该已经锁定了关系，
		 * 我们可以在这里使用 NoLock。
		 */
		fc_rel = table_open(fc_rte->relid, NoLock);

		/*
		 * 收集我们必须应用的 RIR 规则
		 */
		fc_rules = fc_rel->rd_rules;
		if (fc_rules != NULL)
		{
			fc_locks = NIL;
			for (fc_i = 0; fc_i < fc_rules->numLocks; fc_i++)
			{
				fc_rule = fc_rules->rules[fc_i];
				if (fc_rule->event != CMD_SELECT)
					continue;

				fc_locks = lappend(fc_locks, fc_rule);
			}

			/*
			 * 如果我们找到了任何规则，就应用它们 --- 但首先检查递归！
			 */
			if (fc_locks != NIL)
			{
				ListCell   *fc_l;

				if (list_member_oid(fc_activeRIRs, RelationGetRelid(fc_rel)))
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
							 errmsg("infinite recursion detected in rules for relation \"%s\"",
									RelationGetRelationName(fc_rel))));
				fc_activeRIRs = lappend_oid(fc_activeRIRs, RelationGetRelid(fc_rel));

				foreach(fc_l, fc_locks)
				{
					fc_rule = lfirst(fc_l);

					fc_parsetree = fc_ApplyRetrieveRule(fc_parsetree,
												  fc_rule,
												  fc_rt_index,
												  fc_rel,
												  fc_activeRIRs);
				}

				fc_activeRIRs = list_delete_last(fc_activeRIRs);
			}
		}

		table_close(fc_rel, NoLock);
	}

	/* 递归进入 WITH 中的子查询 */
	foreach(fc_lc, fc_parsetree->cteList)
	{
		CommonTableExpr *fc_cte = (CommonTableExpr *) lfirst(fc_lc);

		fc_cte->ctequery = (Node *)
			fc_fireRIRrules((Query *) fc_cte->ctequery, fc_activeRIRs);

		/*
		 * 在这里时，确保如果其任何 CTE 具有行安全性，则查询被标记为具有行安全性。
		 */
		fc_parsetree->hasRowSecurity |= ((Query *) fc_cte->ctequery)->hasRowSecurity;
	}

	/*
	 * 也递归进入子链接子查询。但我们已经处理了
	 * rtable 和 cteList 中的子查询。
	 */
	if (fc_parsetree->hasSubLinks)
	{
		fireRIRonSubLink_context fc_context;

		fc_context.activeRIRs = fc_activeRIRs;
		fc_context.hasRowSecurity = false;

		query_tree_walker(fc_parsetree, fc_fireRIRonSubLink, (void *) &fc_context,
						  QTW_IGNORE_RC_SUBQUERIES);

		/*
		 * 如果其任何子链接有行安全性，确保查询被标记为具有行安全性。
		 */
		fc_parsetree->hasRowSecurity |= fc_context.hasRowSecurity;
	}

	/*
	 * 应用任何行级安全策略。我们最后执行此操作，因为它
	 * 需要特殊的递归检测，如果新的 quals 有子链接
	 * 子查询，如果我们在上面的循环中执行此操作，query_tree_walker 将会
	 * 对这些 quals 进行第二次递归。
	 */
	fc_rt_index = 0;
	foreach(fc_lc, fc_parsetree->rtable)
	{
		RangeTblEntry *fc_rte = (RangeTblEntry *) lfirst(fc_lc);
		Relation	fc_rel;
		List	   *fc_securityQuals;
		List	   *fc_withCheckOptions;
		bool		fc_hasRowSecurity;
		bool		fc_hasSubLinks;

		++fc_rt_index;

		/* 只有普通关系可以具有 RLS 策略 */
		if (fc_rte->rtekind != RTE_RELATION ||
			(fc_rte->relkind != RELKIND_RELATION &&
			 fc_rte->relkind != RELKIND_PARTITIONED_TABLE))
			continue;

		fc_rel = table_open(fc_rte->relid, NoLock);

		/*
		 * 获取必须应用于此 RTE 的任何新安全 quals。
		 */
		get_row_security_policies(fc_parsetree, fc_rte, fc_rt_index,
								  &fc_securityQuals, &fc_withCheckOptions,
								  &fc_hasRowSecurity, &fc_hasSubLinks);

		if (fc_securityQuals != NIL || fc_withCheckOptions != NIL)
		{
			if (fc_hasSubLinks)
			{
				acquireLocksOnSubLinks_context fc_context;
				fireRIRonSubLink_context fc_fire_context;

				/*
				 * 递归处理新的 quals，检查无限
				 * 递归。
				 */
				if (list_member_oid(fc_activeRIRs, RelationGetRelid(fc_rel)))
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
							 errmsg("infinite recursion detected in policy for relation \"%s\"",
									RelationGetRelationName(fc_rel))));

				fc_activeRIRs = lappend_oid(fc_activeRIRs, RelationGetRelid(fc_rel));

				/*
				 * get_row_security_policies 刚刚返回 securityQuals
				 * 和/或 withCheckOptions，并且存在 SubLinks，确保我们锁定
				 * 任何被引用的关系。
				 *
				 * 这些锁通常由解析器获取，但
				 * securityQuals 和 withCheckOptions 在解析后添加。
				 */
				fc_context.for_execute = true;
				(void) fc_acquireLocksOnSubLinks((Node *) fc_securityQuals, &fc_context);
				(void) fc_acquireLocksOnSubLinks((Node *) fc_withCheckOptions,
											  &fc_context);

				/*
				 * 现在我们已经锁定了任何由
				 * get_row_security_policies 添加的东西，触发它们的 RIR 规则。
				 */
				fc_fire_context.activeRIRs = fc_activeRIRs;
				fc_fire_context.hasRowSecurity = false;

				expression_tree_walker((Node *) fc_securityQuals,
									   fc_fireRIRonSubLink, (void *) &fc_fire_context);

				expression_tree_walker((Node *) fc_withCheckOptions,
									   fc_fireRIRonSubLink, (void *) &fc_fire_context);

				/*
				 * 我们可以忽略 fire_context.hasRowSecurity 的值
				 * 因为我们仅在 hasRowSecurity
				 * 已经为真时才会达到此代码。
				 */
				Assert(fc_hasRowSecurity);

				fc_activeRIRs = list_delete_last(fc_activeRIRs);
			}

			/*
			 * 将新的安全屏障 quals 添加到 RTE 的
			 * 列表开头，以便在任何现有的屏障 quals
			 * 之前应用它们（这些屏障 quals 来自
			 * 安全屏障视图，并且应该比表本身的 RLS 条件获得更低的优先级）。
			 */
			fc_rte->securityQuals = list_concat(fc_securityQuals,
											 fc_rte->securityQuals);

			fc_parsetree->withCheckOptions = list_concat(fc_withCheckOptions,
													  fc_parsetree->withCheckOptions);
		}

		/*
		 * 如果行级安全适用，或者如果新的 quals 具有子链接，
		 * 确保查询被正确标记。
		 */
		if (fc_hasRowSecurity)
			fc_parsetree->hasRowSecurity = true;
		if (fc_hasSubLinks)
			fc_parsetree->hasSubLinks = true;

		table_close(fc_rel, NoLock);
	}

	return fc_parsetree;
}



/*
 * 通过将 'AND rule_qual IS NOT TRUE' 添加到其资格中来修改给定的查询。 这用于为条件 INSTEAD 规则生成合适的"else 子句"。 (不幸的是，我们必须使用 "x IS NOT TRUE"，而不仅仅是 "NOT x"，否则在 qual 评估为 NULL 时我们将做错事情。)
 *
 * rule_qual 可能包含对 OLD 或 NEW 的引用。 OLD 引用被替换为对指定 rt_index（规则适用的关系）的引用。 NEW 引用仅适用于对该关系本身的 INSERT 和 UPDATE 查询，因此它们应被替换为查询自身目标列表中相关条目的副本。
 */
static Query * fc_CopyAndAddInvertedQual(Query *fc_parsetree,
					   Node *fc_rule_qual,
					   int fc_rt_index,
					   CmdType fc_event)
{
	/* 不要在传入的 qual 上乱写（它在 relcache 中！） */
	Node	   *fc_new_qual = copyObject(fc_rule_qual);
	acquireLocksOnSubLinks_context fc_context;

	fc_context.for_execute = true;

	/*
	 * 如果 qual 中有子查询，则获取必要的锁并修复任何已删除的 JOIN RTE 条目。 （这在某种程度上与 rewriteRuleAction 有些冗余，但并非完全... 考虑重新结构化以便我们只需处理一次 qual。）
	 */
	(void) fc_acquireLocksOnSubLinks(fc_new_qual, &fc_context);

	/* 修复对 OLD 的引用 */
	ChangeVarNodes(fc_new_qual, PRS2_OLD_VARNO, fc_rt_index, 0);
	/* 修复对 NEW 的引用 */
	if (fc_event == CMD_INSERT || fc_event == CMD_UPDATE)
		fc_new_qual = ReplaceVarsFromTargetList(fc_new_qual,
											 PRS2_NEW_VARNO,
											 0,
											 rt_fetch(fc_rt_index,
													  fc_parsetree->rtable),
											 fc_parsetree->targetList,
											 (fc_event == CMD_UPDATE) ?
											 REPLACEVARS_CHANGE_VARNO :
											 REPLACEVARS_SUBSTITUTE_NULL,
											 fc_rt_index,
											 &fc_parsetree->hasSubLinks);
	/* 并附加修复后的 qual */
	AddInvertedQual(fc_parsetree, fc_new_qual);

	return fc_parsetree;
}


/*
 *	fireRules -
 *	   迭代规则锁，应用规则。
 *
 * 输入参数：
 *	parsetree - 原始查询
 *	rt_index - 原始查询中结果关系的 RT 索引
 *	event - 规则事件的类型
 *	locks - 要触发的规则列表
 * 输出参数：
 *	*instead_flag - 如果发现任何不合格的 INSTEAD 规则，则设置为 true
 *					（必须初始化为 false）
 *	*returning_flag - 如果我们在任何规则中重写 RETURNING 子句，则设置为 true
 *					（必须初始化为 false）
 *	*qual_product - 如果发现任何合格的 INSTEAD 规则，则用修改后的原始查询填充
 *					（必须初始化为 NULL）
 * 返回值：
 *	针对该查询调整后的规则操作列表
 *
 * 合格的 INSTEAD 规则生成其操作时，会添加资格条件。它们还会生成一个修改过的原始查询版本，并添加否定的资格条件，以便它仅适用于合格操作不适用的行。（如果存在多个合格的 INSTEAD 规则，我们将所有的否定资格条件与单个修改后的原始查询进行 AND 操作。）如果找到合格或不合格的 INSTEAD 规则，我们将不会执行原始的未修改查询。如果同时找到二者，则修改后的原始查询也会被丢弃。
 */
static List * fc_fireRules(Query *fc_parsetree,
		  int fc_rt_index,
		  CmdType fc_event,
		  List *fc_locks,
		  bool *fc_instead_flag,
		  bool *fc_returning_flag,
		  Query **fc_qual_product)
{
	List	   *fc_results = NIL;
	ListCell   *fc_l;

	foreach(fc_l, fc_locks)
	{
		RewriteRule *fc_rule_lock = (RewriteRule *) lfirst(fc_l);
		Node	   *fc_event_qual = fc_rule_lock->qual;
		List	   *fc_actions = fc_rule_lock->actions;
		QuerySource fc_qsrc;
		ListCell   *fc_r;

		/* 确定操作的正确 QuerySource 值 */
		if (fc_rule_lock->isInstead)
		{
			if (fc_event_qual != NULL)
				fc_qsrc = QSRC_QUAL_INSTEAD_RULE;
			else
			{
				fc_qsrc = QSRC_INSTEAD_RULE;
				*fc_instead_flag = true;	/* 报告不合格的 INSTEAD */
			}
		}
		else
			fc_qsrc = QSRC_NON_INSTEAD_RULE;

		if (fc_qsrc == QSRC_QUAL_INSTEAD_RULE)
		{
			/*
			 * 如果存在带有资格的 INSTEAD 规则，原始查询仍会执行。
			 * 但所有的 INSTEAD 规则的否定规则资格都将被添加，这样它只会在
			 * 所有 INSTEAD 规则的规则资格为 false 的情况下执行其操作。
			 * 把它想象成案例中的默认操作。我们将其保存在 *qual_product 中，
			 * 以便 RewriteQuery() 可以在我们处理完之后将其添加到查询列表中。
			 *
			 * 如果我们已经找到不合格的 INSTEAD 规则，则 *qual_product 将不再使用，
			 * 因此无需构建它。
			 */
			if (!*fc_instead_flag)
			{
				if (*fc_qual_product == NULL)
					*fc_qual_product = copyObject(fc_parsetree);
				*fc_qual_product = fc_CopyAndAddInvertedQual(*fc_qual_product,
													   fc_event_qual,
													   fc_rt_index,
													   fc_event);
			}
		}

		/* 现在处理规则的操作并将其添加到结果列表中 */
		foreach(fc_r, fc_actions)
		{
			Query	   *fc_rule_action = lfirst(fc_r);

			if (fc_rule_action->commandType == CMD_NOTHING)
				continue;

			fc_rule_action = fc_rewriteRuleAction(fc_parsetree, fc_rule_action,
											fc_event_qual, fc_rt_index, fc_event,
											fc_returning_flag);

			fc_rule_action->querySource = fc_qsrc;
			fc_rule_action->canSetTag = false; /* 可能稍后更改 */

			fc_results = lappend(fc_results, fc_rule_action);
		}
	}

	return fc_results;
}


/*
 * get_view_query - 从视图的 _RETURN 规则中获取查询。
 *
 * 调用者应已验证关系是视图，因此我们应该找到一个 ON SELECT 操作。
 *
 * 请注意，返回的指针是指向 relcache，因此必须视为只读，调用者不能修改或随意更改。
 */
Query * get_view_query(Relation fc_view)
{
	int			fc_i;

	Assert(fc_view->rd_rel->relkind == RELKIND_VIEW);

	for (fc_i = 0; fc_i < fc_view->rd_rules->numLocks; fc_i++)
	{
		RewriteRule *fc_rule = fc_view->rd_rules->rules[fc_i];

		if (fc_rule->event == CMD_SELECT)
		{
			/* 一个 _RETURN 规则应该只有一个操作 */
			if (list_length(fc_rule->actions) != 1)
				elog(ERROR, "invalid _RETURN rule action specification");

			return (Query *) linitial(fc_rule->actions);
		}
	}

	elog(ERROR, "failed to find _RETURN rule for view");
	return NULL;				/* 保持编译器安静 */
}


/*
 * view_has_instead_trigger - 视图是否具有 INSTEAD OF 触发器事件？
 *
 * 如果有，我们不想将其视为可自动更新的。这个测试不能折叠到 view_query_is_auto_updatable 中，因为这不是错误条件。
 */
static bool fc_view_has_instead_trigger(Relation fc_view, CmdType fc_event)
{
	TriggerDesc *fc_trigDesc = fc_view->trigdesc;

	switch (fc_event)
	{
		case CMD_INSERT:
			if (fc_trigDesc && fc_trigDesc->trig_insert_instead_row)
				return true;
			break;
		case CMD_UPDATE:
			if (fc_trigDesc && fc_trigDesc->trig_update_instead_row)
				return true;
			break;
		case CMD_DELETE:
			if (fc_trigDesc && fc_trigDesc->trig_delete_instead_row)
				return true;
			break;
		default:
			elog(ERROR, "unrecognized CmdType: %d", (int) fc_event);
			break;
	}
	return false;
}


/*
 * view_col_is_auto_updatable - 测试视图的指定列是否可自动更新。
 * 返回 NULL（如果该列可以更新）或一个消息字符串，说明无法更新的原因。
 *
 * 返回的字符串未被翻译；如果它作为错误消息显示，调用者应应用 _() 进行翻译。
 *
 * 请注意，此处执行的检查仅限于该视图。我们不检查底层基础关系的引用列是否可更新。
 */
static const char * fc_view_col_is_auto_updatable(RangeTblRef *fc_rtr, TargetEntry *fc_tle)
{
	Var		   *fc_var = (Var *) fc_tle->expr;

	/*
	 * 目前，我们支持的唯一可更新列是指向底层基础关系的用户列的 Vars。
	 *
	 * 视图的目标列表可能包含 resjunk 列（例如，以“SELECT * FROM t ORDER BY a+b”定义的视图是可自动更新的），
	 * 但这些列不是可自动更新的，实际上不应出现在外部查询的目标列表中。
	 */
	if (fc_tle->resjunk)
		return gettext_noop("Junk view columns are not updatable.");

	if (!IsA(fc_var, Var) ||
		fc_var->varno != fc_rtr->rtindex ||
		fc_var->varlevelsup != 0)
		return gettext_noop("View columns that are not columns of their base relation are not updatable.");

	if (fc_var->varattno < 0)
		return gettext_noop("View columns that refer to system columns are not updatable.");

	if (fc_var->varattno == 0)
		return gettext_noop("View columns that return whole-row references are not updatable.");

	return NULL;				/* 视图列是可更新的 */
}


/*
 * view_query_is_auto_updatable - 测试指定的视图定义是否表示可自动更新的视图。
 * 返回 NULL（如果该视图可以更新）或一个消息字符串，说明无法更新的原因。
 *
 * 返回的字符串未被翻译；如果它作为错误消息显示，调用者应应用 _() 进行翻译。
 *
 * 如果 check_cols 为 true，则要求视图至少具有一个可更新的列（这是 INSERT/UPDATE 所必要的）。
 * 否则，视图的列不会被检查是否可更新。另请参见 view_cols_are_auto_updatable。
 *
 * 请注意，此处执行的检查仅基于视图定义。我们不检查视图引用的任何基础关系是否可更新。
 */
const char * view_query_is_auto_updatable(Query *fc_viewquery, bool fc_check_cols)
{
	RangeTblRef *fc_rtr;
	RangeTblEntry *fc_base_rte;

	/*----------
	 * 检查视图是否简单可更新。根据 SQL-92，这意味着：
	 *	- 没有 DISTINCT 子句。
	 *	- 每个 TLE 是列引用，并且每列最多出现一次。
	 *	- FROM 仅包含一个基础关系。
	 *	- 没有 GROUP BY 或 HAVING 子句。
	 *	- 没有集合操作（UNION、INTERSECT 或 EXCEPT）。
	 *	- WHERE 子句中没有引用目标表的子查询。
	 *
	 * 我们忽略最后的限制，因为强制执行会很复杂，
	 * 并且不允许子查询也没有任何实际好处。（标准关注的
	 * 语义问题在 Postgres 中并不存在，因为任何这样的
	 * 子查询都不会看到外部查询执行的任何更新，得益于
	 * MVCC 快照。）
	 *
	 * 我们还通过支持 SQL:1999 特性 T111 放松了第二个限制，
	 * 该特性允许可更新和不可更新列的混合，只要
	 * INSERT 或 UPDATE 不尝试赋值给不可更新列。
	 *
	 * 此外，我们还施加这些约束，涉及不属于 SQL-92 的特性：
	 *	- 没有 CTE（WITH 子句）。
	 *	- 没有 OFFSET 或 LIMIT 子句（这符合 SQL:2008 的限制）。
	 *	- 在 tlist 中没有系统列（包括整行引用）。
	 *	- 在 tlist 中没有窗口函数。
	 *	- 在 tlist 中没有集合返回函数。
	 *
	 * 请注意，我们在不递归展开视图的情况下进行这些检查。
	 * 如果基础关系是视图，我们稍后会递归处理它。
	 *----------
	 */
	if (fc_viewquery->distinctClause != NIL)
		return gettext_noop("Views containing DISTINCT are not automatically updatable.");

	if (fc_viewquery->groupClause != NIL || fc_viewquery->groupingSets)
		return gettext_noop("Views containing GROUP BY are not automatically updatable.");

	if (fc_viewquery->havingQual != NULL)
		return gettext_noop("Views containing HAVING are not automatically updatable.");

	if (fc_viewquery->setOperations != NULL)
		return gettext_noop("Views containing UNION, INTERSECT, or EXCEPT are not automatically updatable.");

	if (fc_viewquery->cteList != NIL)
		return gettext_noop("Views containing WITH are not automatically updatable.");

	if (fc_viewquery->limitOffset != NULL || fc_viewquery->limitCount != NULL)
		return gettext_noop("Views containing LIMIT or OFFSET are not automatically updatable.");

	/*
	 * 我们不能在目标列表中允许窗口函数或集合返回函数。
	 * 否则，我们可能会将它们插入主查询的资格中。
	 * 我们还必须检查目标列表中的聚合，以防它们
	 * 在没有 GROUP BY 的情况下出现。
	 *
	 * 这些限制确保视图的每一行对应于基础关系中的唯一行。
	 */
	if (fc_viewquery->hasAggs)
		return gettext_noop("Views that return aggregate functions are not automatically updatable.");

	if (fc_viewquery->hasWindowFuncs)
		return gettext_noop("Views that return window functions are not automatically updatable.");

	if (fc_viewquery->hasTargetSRFs)
		return gettext_noop("Views that return set-returning functions are not automatically updatable.");

	/*
	 * 视图查询应从单一基础关系中选择，该关系必须是表
	 * 或另一个视图。
	 */
	if (list_length(fc_viewquery->jointree->fromlist) != 1)
		return gettext_noop("Views that do not select from a single table or view are not automatically updatable.");

	fc_rtr = (RangeTblRef *) linitial(fc_viewquery->jointree->fromlist);
	if (!IsA(fc_rtr, RangeTblRef))
		return gettext_noop("Views that do not select from a single table or view are not automatically updatable.");

	fc_base_rte = rt_fetch(fc_rtr->rtindex, fc_viewquery->rtable);
	if (fc_base_rte->rtekind != RTE_RELATION ||
		(fc_base_rte->relkind != RELKIND_RELATION &&
		 fc_base_rte->relkind != RELKIND_FOREIGN_TABLE &&
		 fc_base_rte->relkind != RELKIND_VIEW &&
		 fc_base_rte->relkind != RELKIND_PARTITIONED_TABLE))
		return gettext_noop("Views that do not select from a single table or view are not automatically updatable.");

	if (fc_base_rte->tablesample)
		return gettext_noop("Views containing TABLESAMPLE are not automatically updatable.");

	/*
	 * 检查视图是否至少有一个可更新列。这是 INSERT/UPDATE 的要求，
	 * 但对 DELETE 不是必需的。
	 */
	if (fc_check_cols)
	{
		ListCell   *fc_cell;
		bool		fc_found;

		fc_found = false;
		foreach(fc_cell, fc_viewquery->targetList)
		{
			TargetEntry *fc_tle = (TargetEntry *) lfirst(fc_cell);

			if (fc_view_col_is_auto_updatable(fc_rtr, fc_tle) == NULL)
			{
				fc_found = true;
				break;
			}
		}

		if (!fc_found)
			return gettext_noop("Views that have no updatable columns are not automatically updatable.");
	}

	return NULL;				/* 视图是可更新的 */
}


/*
 * view_cols_are_auto_updatable - 测试自动可更新视图的所有必需列
 * 实际上是否可更新。如果所有必需列均可更新，则返回 NULL，或者
 * 返回一个消息字符串，说明它们不可更新的原因。
 *
 * 返回的字符串未被翻译；如果作为错误消息显示，则调用者
 * 应该应用 _() 进行翻译。
 *
 * 这应在 INSERT/UPDATE 中使用，以确保我们不尝试赋值给
 * 任何不可更新列。
 *
 * 此外，它可用于检索视图中可更新列的集合，或者如果一个或多个
 * 必需列不可更新，则返回第一个违规的不可更新列的名称。
 *
 * 调用者必须已经验证这是一个自动可更新视图
 * 使用 view_query_is_auto_updatable。
 *
 * 请注意，此处执行的检查仅基于视图定义。
 * 我们不检查基础关系的引用列是否可更新。
 */
static const char * fc_view_cols_are_auto_updatable(Query *fc_viewquery,
							 Bitmapset *fc_required_cols,
							 Bitmapset **fc_updatable_cols,
							 char **fc_non_updatable_col)
{
	RangeTblRef *fc_rtr;
	AttrNumber	fc_col;
	ListCell   *fc_cell;

	/*
	 * 调用者应该已经验证该视图是自动可更新的，因此
	 * 应该只有一个基础关系。
	 */
	Assert(list_length(fc_viewquery->jointree->fromlist) == 1);
	fc_rtr = linitial_node(RangeTblRef, fc_viewquery->jointree->fromlist);

	/* 初始化可选返回值 */
	if (fc_updatable_cols != NULL)
		*fc_updatable_cols = NULL;
	if (fc_non_updatable_col != NULL)
		*fc_non_updatable_col = NULL;

	/* 测试每个视图列的可更新性 */
	fc_col = -FirstLowInvalidHeapAttributeNumber;
	foreach(fc_cell, fc_viewquery->targetList)
	{
		TargetEntry *fc_tle = (TargetEntry *) lfirst(fc_cell);
		const char *fc_col_update_detail;

		fc_col++;
		fc_col_update_detail = fc_view_col_is_auto_updatable(fc_rtr, fc_tle);

		if (fc_col_update_detail == NULL)
		{
			/* 该列是可更新的 */
			if (fc_updatable_cols != NULL)
				*fc_updatable_cols = bms_add_member(*fc_updatable_cols, fc_col);
		}
		else if (bms_is_member(fc_col, fc_required_cols))
		{
			/* 必需列不可更新 */
			if (fc_non_updatable_col != NULL)
				*fc_non_updatable_col = fc_tle->resname;
			return fc_col_update_detail;
		}
	}

	return NULL;				/* 所有必需的视图列均可更新 */
}



/*
 * relation_is_updatable - 确定指定关系支持哪些更新事件。
 *
 * 请注意，视图可能包含可更新和不可更新列的混合。为了使视图支持 INSERT/UPDATE，必须至少有一个可更新列，但 DELETE 没有这样的限制。如果 include_cols 非 NULL，则仅在测试可更新性时考虑指定的列。
 *
 * 与前面的函数不同，这个函数会递归查看视图的基关系，因此需要检测递归。为此，我们传递一个当前考虑的外部关系列表。外部调用者只需传递 NIL。
 *
 * 这用于 information_schema 视图，这些视图有“可更新”和“触发器可更新”的独立概念。如果关系可以在无需触发器的情况下被更新（要么因为它有合适的规则，要么因为它简单到可以自动更新），则该关系是“可更新的”。如果关系有合适的 INSTEAD OF 触发器，则该关系是“触发器可更新的”。SQL 标准认为这不一定是可更新的，可能是因为无法知道触发器将实际执行什么。因此，information_schema 视图在调用此函数时使用 include_triggers = false。然而，其他调用者可能只关心数据修改 SQL 是否可用，因此可以传递 include_triggers = true，将触发器可更新性包含在结果中。
 *
 * 返回值是规则事件编号的位掩码，指示支持哪些 INSERT、UPDATE 和 DELETE 操作。（我们以这种方式进行，以便能够在一次调用中测试对 UPDATE 和 DELETE 的支持。）
 */
int relation_is_updatable(Oid fc_reloid,
					  List *fc_outer_reloids,
					  bool fc_include_triggers,
					  Bitmapset *fc_include_cols)
{
	int			fc_events = 0;
	Relation	fc_rel;
	RuleLock   *fc_rulelocks;

#define ALL_EVENTS ((1 << CMD_INSERT) | (1 << CMD_UPDATE) | (1 << CMD_DELETE))

	/* 由于此函数是递归的，它可能会导致栈溢出 */
	check_stack_depth();

	fc_rel = try_relation_open(fc_reloid, AccessShareLock);

	/*
	 * 如果关系不存在，则返回零，而不是抛出错误。这很有帮助，因为在 MVCC 规则下扫描 information_schema 视图可能会导致引用实际上已经被删除的关系。
	 */
	if (fc_rel == NULL)
		return 0;

	/* 如果我们检测到递归视图，报告它不可更新 */
	if (list_member_oid(fc_outer_reloids, RelationGetRelid(fc_rel)))
	{
		relation_close(fc_rel, AccessShareLock);
		return 0;
	}

	/* 如果关系是一个表，它总是可更新的 */
	if (fc_rel->rd_rel->relkind == RELKIND_RELATION ||
		fc_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
	{
		relation_close(fc_rel, AccessShareLock);
		return ALL_EVENTS;
	}

	/* 查找无条件的DO INSTEAD规则，并记录支持的事件 */
	fc_rulelocks = fc_rel->rd_rules;
	if (fc_rulelocks != NULL)
	{
		int			fc_i;

		for (fc_i = 0; fc_i < fc_rulelocks->numLocks; fc_i++)
		{
			if (fc_rulelocks->rules[fc_i]->isInstead &&
				fc_rulelocks->rules[fc_i]->qual == NULL)
			{
				fc_events |= ((1 << fc_rulelocks->rules[fc_i]->event) & ALL_EVENTS);
			}
		}

		/* 如果我们对所有事件都有规则，我们就完成了 */
		if (fc_events == ALL_EVENTS)
		{
			relation_close(fc_rel, AccessShareLock);
			return fc_events;
		}
	}

	/* 同样查找INSTEAD OF触发器，如果它们要被包括 */
	if (fc_include_triggers)
	{
		TriggerDesc *fc_trigDesc = fc_rel->trigdesc;

		if (fc_trigDesc)
		{
			if (fc_trigDesc->trig_insert_instead_row)
				fc_events |= (1 << CMD_INSERT);
			if (fc_trigDesc->trig_update_instead_row)
				fc_events |= (1 << CMD_UPDATE);
			if (fc_trigDesc->trig_delete_instead_row)
				fc_events |= (1 << CMD_DELETE);

			/* 如果我们对所有事件都有触发器，我们就完成了 */
			if (fc_events == ALL_EVENTS)
			{
				relation_close(fc_rel, AccessShareLock);
				return fc_events;
			}
		}
	}

	/* 如果这是一个外部表，检查它支持哪些更新事件 */
	if (fc_rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
	{
		FdwRoutine *fc_fdwroutine = GetFdwRoutineForRelation(fc_rel, false);

		if (fc_fdwroutine->IsForeignRelUpdatable != NULL)
			fc_events |= fc_fdwroutine->IsForeignRelUpdatable(fc_rel);
		else
		{
			/* 假定执行器函数的存在是足够的 */
			if (fc_fdwroutine->ExecForeignInsert != NULL)
				fc_events |= (1 << CMD_INSERT);
			if (fc_fdwroutine->ExecForeignUpdate != NULL)
				fc_events |= (1 << CMD_UPDATE);
			if (fc_fdwroutine->ExecForeignDelete != NULL)
				fc_events |= (1 << CMD_DELETE);
		}

		relation_close(fc_rel, AccessShareLock);
		return fc_events;
	}

	/* 检查这是否是一个自动可更新的视图 */
	if (fc_rel->rd_rel->relkind == RELKIND_VIEW)
	{
		Query	   *fc_viewquery = get_view_query(fc_rel);

		if (view_query_is_auto_updatable(fc_viewquery, false) == NULL)
		{
			Bitmapset  *fc_updatable_cols;
			int			fc_auto_events;
			RangeTblRef *fc_rtr;
			RangeTblEntry *fc_base_rte;
			Oid			fc_baseoid;

			/*
			 * 确定视图的哪些列是可更新的。如果在我们查看的
			 * 列集中没有可更新的列，则该视图不支持INSERT/UPDATE，
			 * 但它可能仍然支持DELETE。
			 */
			fc_view_cols_are_auto_updatable(fc_viewquery, NULL,
										 &fc_updatable_cols, NULL);

			if (fc_include_cols != NULL)
				fc_updatable_cols = bms_int_members(fc_updatable_cols, fc_include_cols);

			if (bms_is_empty(fc_updatable_cols))
				fc_auto_events = (1 << CMD_DELETE);	/* 可能支持DELETE */
			else
				fc_auto_events = ALL_EVENTS;	/* 可能支持所有事件 */

			/*
			 * 基础关系也必须支持这些更新命令。
			 * 表始终是可更新的，但对于任何其他类型的基础关系，
			 * 我们必须对通过该视图中的本地可更新列引用的列进行递归检查。
			 */
			fc_rtr = (RangeTblRef *) linitial(fc_viewquery->jointree->fromlist);
			fc_base_rte = rt_fetch(fc_rtr->rtindex, fc_viewquery->rtable);
			Assert(fc_base_rte->rtekind == RTE_RELATION);

			if (fc_base_rte->relkind != RELKIND_RELATION &&
				fc_base_rte->relkind != RELKIND_PARTITIONED_TABLE)
			{
				fc_baseoid = fc_base_rte->relid;
				fc_outer_reloids = lappend_oid(fc_outer_reloids,
											RelationGetRelid(fc_rel));
				fc_include_cols = fc_adjust_view_column_set(fc_updatable_cols,
													  fc_viewquery->targetList);
				fc_auto_events &= relation_is_updatable(fc_baseoid,
													 fc_outer_reloids,
													 fc_include_triggers,
													 fc_include_cols);
				fc_outer_reloids = list_delete_last(fc_outer_reloids);
			}
			fc_events |= fc_auto_events;
		}
	}

	/* 如果我们到这里，关系可能支持某些更新命令 */
	relation_close(fc_rel, AccessShareLock);
	return fc_events;
}


/*
 * adjust_view_column_set - 根据目标列映射一组列编号
 *
 * 这用于简单可更新视图，将列权限集映射到底层基础关系中的匹配列。
 * 目标列中的相关条目必须是底层关系的普通变量
 * （如上面在view_query_is_auto_updatable中的检查）。
 */
static Bitmapset * fc_adjust_view_column_set(Bitmapset *fc_cols, List *fc_targetlist)
{
	Bitmapset  *fc_result = NULL;
	int			fc_col;

	fc_col = -1;
	while ((fc_col = bms_next_member(fc_cols, fc_col)) >= 0)
	{
		/* 位数由 FirstLowInvalidHeapAttributeNumber 偏移 */
		AttrNumber	fc_attno = fc_col + FirstLowInvalidHeapAttributeNumber;

		if (fc_attno == InvalidAttrNumber)
		{
			/*
			 * 视图有一个整行引用。出于权限目的，
			 * 将其视为对视图中每个可用列的引用。
			 * （我们不应该将其转换为对基础关系的整行引用，
			 * 因为视图可能不会触及基础关系的所有列。）
			 */
			ListCell   *fc_lc;

			foreach(fc_lc, fc_targetlist)
			{
				TargetEntry *fc_tle = lfirst_node(TargetEntry, fc_lc);
				Var		   *fc_var;

				if (fc_tle->resjunk)
					continue;
				fc_var = castNode(Var, fc_tle->expr);
				fc_result = bms_add_member(fc_result,
										fc_var->varattno - FirstLowInvalidHeapAttributeNumber);
			}
		}
		else
		{
			/*
			 * 视图没有系统列，因此我们不期望在这里看到
			 * 任何其他系统属性。如果我们发现一个，
			 * 错误情况将适用。
			 */
			TargetEntry *fc_tle = get_tle_by_resno(fc_targetlist, fc_attno);

			if (fc_tle != NULL && !fc_tle->resjunk && IsA(fc_tle->expr, Var))
			{
				Var		   *fc_var = (Var *) fc_tle->expr;

				fc_result = bms_add_member(fc_result,
										fc_var->varattno - FirstLowInvalidHeapAttributeNumber);
			}
			else
				elog(ERROR, "attribute number %d not found in view targetlist",
					 fc_attno);
		}
	}

	return fc_result;
}


/*
 * rewriteTargetView -
 *	  尝试重写目标关系为视图的查询，以便
 *	  视图的基础关系成为目标关系。
 *
 * 请注意，这里的基础关系本身可能也是一个视图，
 * 该视图可能有或没有INSTEAD OF触发器或规则来处理更新。
 * 这通过RewriteQuery中的递归处理。
 */
static Query * fc_rewriteTargetView(Query *fc_parsetree, Relation fc_view)
{
	Query	   *fc_viewquery;
	const char *fc_auto_update_detail;
	RangeTblRef *fc_rtr;
	int			fc_base_rt_index;
	int			fc_new_rt_index;
	RangeTblEntry *fc_base_rte;
	RangeTblEntry *fc_view_rte;
	RangeTblEntry *fc_new_rte;
	Relation	fc_base_rel;
	List	   *fc_view_targetlist;
	ListCell   *fc_lc;

	/*
	 * 从视图的ON SELECT规则获取查询。我们将修改
	 * 查询，将视图的基础关系更改为目标关系，
	 * 并在此过程中进行各种其他更改，因此我们需要对其进行
	 * 复制（get_view_query()返回指向relcache的指针，
	 * 所以我们必须将其视为只读）。
	 */
	fc_viewquery = copyObject(get_view_query(fc_view));

	/* 在外部查询中定位描述视图的RTE */
	fc_view_rte = rt_fetch(fc_parsetree->resultRelation, fc_parsetree->rtable);

	/* 视图必须是可更新的，否则失败 */
	fc_auto_update_detail =
		view_query_is_auto_updatable(fc_viewquery,
									 fc_parsetree->commandType != CMD_DELETE);

	if (fc_auto_update_detail)
	{
		/* 此处的消息应与execMain.c的CheckValidResultRel匹配 */
		switch (fc_parsetree->commandType)
		{
			case CMD_INSERT:
				ereport(ERROR,
						(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
						 errmsg("cannot insert into view \"%s\"",
								RelationGetRelationName(fc_view)),
						 errdetail_internal("%s", _(fc_auto_update_detail)),
						 errhint("To enable inserting into the view, provide an INSTEAD OF INSERT trigger or an unconditional ON INSERT DO INSTEAD rule.")));
				break;
			case CMD_UPDATE:
				ereport(ERROR,
						(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
						 errmsg("cannot update view \"%s\"",
								RelationGetRelationName(fc_view)),
						 errdetail_internal("%s", _(fc_auto_update_detail)),
						 errhint("To enable updating the view, provide an INSTEAD OF UPDATE trigger or an unconditional ON UPDATE DO INSTEAD rule.")));
				break;
			case CMD_DELETE:
				ereport(ERROR,
						(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
						 errmsg("cannot delete from view \"%s\"",
								RelationGetRelationName(fc_view)),
						 errdetail_internal("%s", _(fc_auto_update_detail)),
						 errhint("To enable deleting from the view, provide an INSTEAD OF DELETE trigger or an unconditional ON DELETE DO INSTEAD rule.")));
				break;
			default:
				elog(ERROR, "unrecognized CmdType: %d",
					 (int) fc_parsetree->commandType);
				break;
		}
	}

	/* 检查是否限制了非系统视图的扩展 */
	if (unlikely((restrict_nonsystem_relation_kind & RESTRICT_RELKIND_VIEW) != 0 &&
				 RelationGetRelid(fc_view) >= FirstNormalObjectId))
		ereport(ERROR,
				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
				 errmsg("access to non-system view \"%s\" is restricted",
						RelationGetRelationName(fc_view))));

	/*
	 * 对于INSERT/UPDATE，修改过的列必须都是可更新的。
	 */
	if (fc_parsetree->commandType != CMD_DELETE)
	{
		Bitmapset  *fc_modified_cols;
		char	   *fc_non_updatable_col;

		/*
		 * 计算修改列的集合，作为结果RTE的insertedCols和/或
		 * updatedCols集中的列以及查询的目标列集的目标。
		 * 我们必须考虑查询的目标列，因为rewriteTargetListIU可能
		 * 已为视图默认值添加了其他目标列表条目，这些条目也必须是
		 * 可更新的。但rewriteTargetListIU也可以删除条目
		 * 如果它们是DEFAULT标记且该列的默认值为NULL，
		 * 因此仅考虑目标列表也是错误的。
		 */
		fc_modified_cols = bms_union(fc_view_rte->insertedCols,
								  fc_view_rte->updatedCols);

		foreach(fc_lc, fc_parsetree->targetList)
		{
			TargetEntry *fc_tle = (TargetEntry *) lfirst(fc_lc);

			if (!fc_tle->resjunk)
				fc_modified_cols = bms_add_member(fc_modified_cols,
											   fc_tle->resno - FirstLowInvalidHeapAttributeNumber);
		}

		if (fc_parsetree->onConflict)
		{
			foreach(fc_lc, fc_parsetree->onConflict->onConflictSet)
			{
				TargetEntry *fc_tle = (TargetEntry *) lfirst(fc_lc);

				if (!fc_tle->resjunk)
					fc_modified_cols = bms_add_member(fc_modified_cols,
												   fc_tle->resno - FirstLowInvalidHeapAttributeNumber);
			}
		}

		fc_auto_update_detail = fc_view_cols_are_auto_updatable(fc_viewquery,
														  fc_modified_cols,
														  NULL,
														  &fc_non_updatable_col);
		if (fc_auto_update_detail)
		{
			/*
			 * 这是一个不同的错误，因试图更新
			 * 在其他可更新视图中不可更新的列而引起。
			 */
			switch (fc_parsetree->commandType)
			{
				case CMD_INSERT:
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("cannot insert into column \"%s\" of view \"%s\"",
									fc_non_updatable_col,
									RelationGetRelationName(fc_view)),
							 errdetail_internal("%s", _(fc_auto_update_detail))));
					break;
				case CMD_UPDATE:
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("cannot update column \"%s\" of view \"%s\"",
									fc_non_updatable_col,
									RelationGetRelationName(fc_view)),
							 errdetail_internal("%s", _(fc_auto_update_detail))));
					break;
				default:
					elog(ERROR, "unrecognized CmdType: %d",
						 (int) fc_parsetree->commandType);
					break;
			}
		}
	}

	/*
	 * 如果我们到这里，view_query_is_auto_updatable()已验证视图
	 * 包含单一的基础关系。
	 */
	Assert(list_length(fc_viewquery->jointree->fromlist) == 1);
	fc_rtr = linitial_node(RangeTblRef, fc_viewquery->jointree->fromlist);

	fc_base_rt_index = fc_rtr->rtindex;
	fc_base_rte = rt_fetch(fc_base_rt_index, fc_viewquery->rtable);
	Assert(fc_base_rte->rtekind == RTE_RELATION);

	/*
	 * 到现在为止，基础关系在我们的查询中完全没有被触碰。
	 * 我们需要在尝试对其执行任何操作之前先获取锁。
	 * （随后的RewriteQuery递归调用将假设我们
	 * 已经拥有正确的锁！）因为它将成为查询的
	 * 目标关系，RowExclusiveLock始终是正确的选择。
	 */
	fc_base_rel = table_open(fc_base_rte->relid, RowExclusiveLock);

	/*
	 * 当我们打开关系时，更新RTE的relkind，以防
	 * 自从创建此视图以来它已更改（参见AcquireRewriteLocks）。
	 */
	fc_base_rte->relkind = fc_base_rel->rd_rel->relkind;

	/*
	 * 如果视图查询包含任何子链接的子查询，
	 * 则我们还需要对它们引用的任何关系获取锁。
	 * 我们知道范围表或CTE中不会有任何子查询，
	 * 因此可以跳过这些，如AcquireRewriteLocks中所述。
	 */
	if (fc_viewquery->hasSubLinks)
	{
		acquireLocksOnSubLinks_context fc_context;

		fc_context.for_execute = true;
		query_tree_walker(fc_viewquery, fc_acquireLocksOnSubLinks, &fc_context,
						  QTW_IGNORE_RC_SUBQUERIES);
	}

	
/*
	 * 创建一个新的目标 RTE 描述基本关系，并将其添加到
	 * 外部查询的 rangetable。（接下来几步发生的事情
	 * 非常像规划器将视图“拉入”外部查询所做的。
	 * 也许有一天我们应该重构足够的东西，以便
	 * 我们可以与规划器共享代码。）
	 *
	 * 确保为目标表设置正确的 rellockmode。
	 * 由于我们已经复制了上面的整个 viewquery，
	 * 因此我们可以直接修改 base_rte 而不是复制它。
	 */
	fc_new_rte = fc_base_rte;
	fc_new_rte->rellockmode = RowExclusiveLock;

	fc_parsetree->rtable = lappend(fc_parsetree->rtable, fc_new_rte);
	fc_new_rt_index = list_length(fc_parsetree->rtable);

	/*
	 * INSERT 从不继承。对于 UPDATE/DELETE，我们使用视图查询的
	 * 继承标志作为基本关系。
	 */
	if (fc_parsetree->commandType == CMD_INSERT)
		fc_new_rte->inh = false;

	/*
	 * 调整视图的目标列表变量，以引用新的目标 RTE，即
	 * 使它们的 varnos 变为 new_rt_index 而不是 base_rt_index。目标列表中
	 * 不能有其他关系的变量，因此这足以将目标列表表达式提取以供外部查询使用。
	 * 目标列表将提供 ReplaceVarsFromTargetList
	 * 中使用的替代表达式。
	 */
	fc_view_targetlist = fc_viewquery->targetList;

	ChangeVarNodes((Node *) fc_view_targetlist,
				   fc_base_rt_index,
				   fc_new_rt_index,
				   0);

	/*
	 * 如果视图设置了“security_invoker”，请标记新目标 RTE 进行
	 * 我们希望对查询调用者强制执行的权限检查。
	 * 否则，我们希望对视图所有者进行强制执行。
	 *
	 * 在关系级别上，需要查询调用者在视图上需要的相同 INSERT/UPDATE/DELETE
	 * 权限。我们删除新 RTE->requiredPerms 中最初显然存在的 ACL_SELECT 位。
	 *
	 * 注意：原始视图 RTE 保留在查询的 rangetable 列表中。
	 * 尽管它在查询计划中将无效，我们仍需要它在这里，以便
	 * 执行器仍然为查询调用者使用视图执行适当的权限检查。
	 */
	if (RelationHasSecurityInvoker(fc_view))
		fc_new_rte->checkAsUser = InvalidOid;
	else
		fc_new_rte->checkAsUser = fc_view->rd_rel->relowner;

	fc_new_rte->requiredPerms = fc_view_rte->requiredPerms;

	/*
	 * 现在处理每列的权限位。
	 *
	 * 最初，new_rte 包含视图引用的所有基础关系列的 selectedCols 权限检查位，
	 * 但由于视图是一个 SELECT 查询，因此其 insertedCols/updatedCols 是空的。
	 * 我们将 insertedCols 和 updatedCols 设置为包括外部查询试图修改的所有列，按需调整
	 * 列编号。但我们将 selectedCols 保持不变，因此视图所有者必须对视图定义中使用的所有
	 * 列具有读取权限，即使外部查询没有读取其中一些列。
	 * 我们可以尝试将 selectedCols 限制为仅转换查询中使用的列，
	 * 但这并不对应于在视图的普通 SELECT 使用中发生的情况：
	 * 所有引用的列必须具有读取权限，即使优化发现其中一些可以
	 * 在查询转换期间被丢弃。我们在这里进行的扁平化也是一种可选优化。
	 * （如果您不信服并希望更改此内容，请注意，将 adjust_view_column_set 应用于
	 * view_rte->selectedCols 显然*不是*正确的答案，因为这忽视了在视图的 WHERE 资格中使用的
	 * 基础关系列。）
	 *
	 * 此步骤需要修改后的视图目标列表，因此我们必须按此顺序进行操作。
	 */
	Assert(bms_is_empty(fc_new_rte->insertedCols) &&
		   bms_is_empty(fc_new_rte->updatedCols));

	fc_new_rte->insertedCols = fc_adjust_view_column_set(fc_view_rte->insertedCols,
												   fc_view_targetlist);

	fc_new_rte->updatedCols = fc_adjust_view_column_set(fc_view_rte->updatedCols,
												  fc_view_targetlist);

	/*
	 * 将视图 RTE 中的任何安全障碍限制条件移到新的目标
	 * RTE。任何这样的限制条件现在应适用于新的目标 RTE，且在重写的查询中
	 * 不会引用原始视图 RTE。
	 */
	fc_new_rte->securityQuals = fc_view_rte->securityQuals;
	fc_view_rte->securityQuals = NIL;

	/*
	 * 现在更新外部查询中引用视图的所有变量，以
	 * 引用基础关系的适当列。
	 */
	fc_parsetree = (Query *)
		ReplaceVarsFromTargetList((Node *) fc_parsetree,
								  fc_parsetree->resultRelation,
								  0,
								  fc_view_rte,
								  fc_view_targetlist,
								  REPLACEVARS_REPORT_ERROR,
								  0,
								  NULL);

	/*
	 * 更新查询中指向视图的所有其他 RTI 引用
	 * （例如，parsetree->resultRelation 本身）以指向新的
	 * 基础关系。变量将不会受到影响，因为它们不再引用
	 * parsetree->resultRelation。
	 */
	ChangeVarNodes((Node *) fc_parsetree,
				   fc_parsetree->resultRelation,
				   fc_new_rt_index,
				   0);
	Assert(fc_parsetree->resultRelation == fc_new_rt_index);

	/*
	 * 对于 INSERT/UPDATE，我们还必须更新目标列表中的 resnos 以引用
	 * 基础关系的列，因为这些指示要影响的目标
	 * 列。
	 *
	 * 注意，这会破坏目标列表的 resno 排序，但在我们递归
	 * 通过 RewriteQuery 时会修复此问题，这会再次在更新的目标列表上
	 * 调用 rewriteTargetListIU。
	 */
	if (fc_parsetree->commandType != CMD_DELETE)
	{
		foreach(fc_lc, fc_parsetree->targetList)
		{
			TargetEntry *fc_tle = (TargetEntry *) lfirst(fc_lc);
			TargetEntry *fc_view_tle;

			if (fc_tle->resjunk)
				continue;

			fc_view_tle = get_tle_by_resno(fc_view_targetlist, fc_tle->resno);
			if (fc_view_tle != NULL && !fc_view_tle->resjunk && IsA(fc_view_tle->expr, Var))
				fc_tle->resno = ((Var *) fc_view_tle->expr)->varattno;
			else
				elog(ERROR, "attribute number %d not found in view targetlist",
					 fc_tle->resno);
		}
	}

	/*
	 * 对于 INSERT .. ON CONFLICT .. DO UPDATE，我们还必须更新 onConflict 数据结构中的各种
	 * 内容。
	 */
	if (fc_parsetree->onConflict &&
		fc_parsetree->onConflict->action == ONCONFLICT_UPDATE)
	{
		Index		fc_old_exclRelIndex,
					fc_new_exclRelIndex;
		ParseNamespaceItem *fc_new_exclNSItem;
		RangeTblEntry *fc_new_exclRte;
		List	   *fc_tmp_tlist;

		/*
		 * 与上述 INSERT/UPDATE 代码类似，更新辅助 UPDATE 目标列表中的 resnos 以引用
		 * 基础关系的列。
		 */
		foreach(fc_lc, fc_parsetree->onConflict->onConflictSet)
		{
			TargetEntry *fc_tle = (TargetEntry *) lfirst(fc_lc);
			TargetEntry *fc_view_tle;

			if (fc_tle->resjunk)
				continue;

			fc_view_tle = get_tle_by_resno(fc_view_targetlist, fc_tle->resno);
			if (fc_view_tle != NULL && !fc_view_tle->resjunk && IsA(fc_view_tle->expr, Var))
				fc_tle->resno = ((Var *) fc_view_tle->expr)->varattno;
			else
				elog(ERROR, "attribute number %d not found in view targetlist",
					 fc_tle->resno);
		}

		/*
		 * 此外，为 EXCLUDED 假关系创建一个新的 RTE，使用查询的新基础关系
		 * （这可能与视图有不同的列列表，因此我们需要一个新的列别名列表）。
		 * 这应与 transformOnConflictClause 匹配。特别注意，relkind 设置为 composite，
		 * 以表示我们并不处理实际关系，并且不需要权限检查。
		 */
		fc_old_exclRelIndex = fc_parsetree->onConflict->exclRelIndex;

		fc_new_exclNSItem = addRangeTableEntryForRelation(make_parsestate(NULL),
													   fc_base_rel,
													   RowExclusiveLock,
													   makeAlias("excluded", NIL),
													   false, false);
		fc_new_exclRte = fc_new_exclNSItem->p_rte;
		fc_new_exclRte->relkind = RELKIND_COMPOSITE_TYPE;
		fc_new_exclRte->requiredPerms = 0;
		/* new_exclRte 中的其他权限字段已为空 */

		fc_parsetree->rtable = lappend(fc_parsetree->rtable, fc_new_exclRte);
		fc_new_exclRelIndex = fc_parsetree->onConflict->exclRelIndex =
			list_length(fc_parsetree->rtable);

		/*
		 * 用一个新的目标列表替换 EXCLUDED 假关系的目标列表，表示来自新基础关系的列。
		 */
		fc_parsetree->onConflict->exclRelTlist =
			BuildOnConflictExcludedTargetlist(fc_base_rel, fc_new_exclRelIndex);

		/*
		 * 更新 ON CONFLICT 子句中引用旧 EXCLUDED 假关系的所有变量。
		 * 我们希望使用视图目标列表中定义的列映射，但我们需要输出引用
		 * 新的 EXCLUDED 假关系，而不是新的目标 RTE。
		 * 还要注意，“EXCLUDED.*” 将使用视图的行类型扩展，这似乎是正确的。
		 */
		fc_tmp_tlist = copyObject(fc_view_targetlist);

		ChangeVarNodes((Node *) fc_tmp_tlist, fc_new_rt_index,
					   fc_new_exclRelIndex, 0);

		fc_parsetree->onConflict = (OnConflictExpr *)
			ReplaceVarsFromTargetList((Node *) fc_parsetree->onConflict,
									  fc_old_exclRelIndex,
									  0,
									  fc_view_rte,
									  fc_tmp_tlist,
									  REPLACEVARS_REPORT_ERROR,
									  0,
									  &fc_parsetree->hasSubLinks);
	}

	
/*
	 * 对于 UPDATE/DELETE，从视图中提取任何 WHERE 条件。我们知道
	 * 条件中的任何 Vars 必须引用一个基本关系，因此我们只需将它们的 varnos 调整为引用新目标（就像
	 * 我们对视图目标列表所做的那样）。
	 *
	 * 如果这是一个安全屏障视图，它的 WHERE 条件必须在外部查询的条件之前应用，
	 * 因此我们将它们作为安全屏障条件附加到 RTE，而不是将它们添加到主 WHERE 子句中。
	 *
	 * 对于 INSERT，视图的条件可以在主查询中忽略。
	 */
	if (fc_parsetree->commandType != CMD_INSERT &&
		fc_viewquery->jointree->quals != NULL)
	{
		Node	   *fc_viewqual = (Node *) fc_viewquery->jointree->quals;

		/*
		 * 即使我们已经在这个函数的顶部复制了 viewquery，
		 * 我们仍然必须在这里再次复制 viewqual，因为我们可能需要在下面的 WithCheckOption 子句中再次使用条件。
		 */
		fc_viewqual = copyObject(fc_viewqual);

		ChangeVarNodes(fc_viewqual, fc_base_rt_index, fc_new_rt_index, 0);

		if (RelationIsSecurityView(fc_view))
		{
			/*
			 * 视图的条件位于现有屏障条件之前：这些条件
			 * 将来自外部的安全屏障视图，因此必须稍后评估。
			 *
			 * 注意：parsetree 已被修改，因此 new_rte 指针是
			 * 过时的，需要重新计算。
			 */
			fc_new_rte = rt_fetch(fc_new_rt_index, fc_parsetree->rtable);
			fc_new_rte->securityQuals = lcons(fc_viewqual, fc_new_rte->securityQuals);

			/*
			 * 不要设置 parsetree->hasRowSecurity，因为这些不是 RLS
			 * 条件（它们不受启用/禁用 RLS 的影响）。
			 */

			/*
			 * 如果添加的条件有子链接，请确保查询被正确标记。
			 */
			if (!fc_parsetree->hasSubLinks)
				fc_parsetree->hasSubLinks = checkExprHasSubLink(fc_viewqual);
		}
		else
			AddQual(fc_parsetree, (Node *) fc_viewqual);
	}

	/*
	 * 对于 INSERT/UPDATE，如果视图具有 WITH CHECK OPTION，或任何
	 * 父视图指定了 WITH CASCADED CHECK OPTION，将视图中的条件
	 * 添加到查询的 withCheckOptions 列表中。
	 */
	if (fc_parsetree->commandType != CMD_DELETE)
	{
		bool		fc_has_wco = RelationHasCheckOption(fc_view);
		bool		fc_cascaded = RelationHasCascadedCheckOption(fc_view);

		/*
		 * 如果父视图具有级联检查选项，则将此视图视为
		 * 它也具有级联检查选项。
		 *
		 * 新的 WithCheckOptions 被添加到列表的开头，因此如果
		 * 存在级联检查选项，它将是列表中的第一个项。
		 */
		if (fc_parsetree->withCheckOptions != NIL)
		{
			WithCheckOption *fc_parent_wco =
			(WithCheckOption *) linitial(fc_parsetree->withCheckOptions);

			if (fc_parent_wco->cascaded)
			{
				fc_has_wco = true;
				fc_cascaded = true;
			}
		}

		/*
		 * 将新的 WithCheckOption 添加到列表的开头，以便
		 * 在内视图上进行检查，再在外视图上进行检查，这符合
		 * SQL 标准的要求。
		 *
		 * 如果新检查是 CASCADED，即使此视图没有条件，我们也需要添加
		 * 它，因为子视图上可能有条件。对于没有条件的视图，可以省略 
		 * LOCAL 检查。
		 */
		if (fc_has_wco && (fc_cascaded || fc_viewquery->jointree->quals != NULL))
		{
			WithCheckOption *fc_wco;

			fc_wco = makeNode(WithCheckOption);
			fc_wco->kind = WCO_VIEW_CHECK;
			fc_wco->relname = pstrdup(RelationGetRelationName(fc_view));
			fc_wco->polname = NULL;
			fc_wco->qual = NULL;
			fc_wco->cascaded = fc_cascaded;

			fc_parsetree->withCheckOptions = lcons(fc_wco,
												fc_parsetree->withCheckOptions);

			if (fc_viewquery->jointree->quals != NULL)
			{
				fc_wco->qual = (Node *) fc_viewquery->jointree->quals;
				ChangeVarNodes(fc_wco->qual, fc_base_rt_index, fc_new_rt_index, 0);

				/*
				 * 如果添加的条件有子链接，请确保查询被正确标记。
				 * 如果查询已经被标记，或者命令是 UPDATE，则可以跳过
				 * 此检查，在这种情况下，相同的条件将已经被添加，此
				 * 检查也将已经完成。
				 */
				if (!fc_parsetree->hasSubLinks &&
					fc_parsetree->commandType != CMD_UPDATE)
					fc_parsetree->hasSubLinks = checkExprHasSubLink(fc_wco->qual);
			}
		}
	}

	table_close(fc_base_rel, NoLock);

	return fc_parsetree;
}


/*
 * RewriteQuery -
 *	  重写查询并在重写的查询上再次应用规则
 *
 * rewrite_events 是一个开放查询重写操作的列表，以便我们可以检测
 * 无限递归。
 *
 * orig_rt_length 是源查询的 rtable 的长度，对于由 fireRules()
 * 创建的产品查询为 0。用于跳过原查询中任何已处理的 VALUES RTEs。
 */
static List * fc_RewriteQuery(Query *fc_parsetree, List *fc_rewrite_events, int fc_orig_rt_length)
{
	CmdType		fc_event = fc_parsetree->commandType;
	bool		fc_instead = false;
	bool		fc_returning = false;
	bool		fc_updatableview = false;
	Query	   *fc_qual_product = NULL;
	List	   *fc_rewritten = NIL;
	ListCell   *fc_lc1;

	/*
	 * 首先，递归处理 WITH 子句中的任何插入/更新/删除语句。
	 * (我们必须首先执行此操作，因为 WITH 子句可能会被复制到
	 * 下面的规则操作中。)
	 */
	foreach(fc_lc1, fc_parsetree->cteList)
	{
		CommonTableExpr *fc_cte = lfirst_node(CommonTableExpr, fc_lc1);
		Query	   *fc_ctequery = castNode(Query, fc_cte->ctequery);
		List	   *fc_newstuff;

		if (fc_ctequery->commandType == CMD_SELECT)
			continue;

		fc_newstuff = fc_RewriteQuery(fc_ctequery, fc_rewrite_events, 0);

		/*
		 * 目前我们只能正确处理无条件的单语句 DO
		 * INSTEAD 规则；我们必须从重写操作中获得恰好一个非实用
		 * 查询，以便填充回 CTE 节点。
		 */
		if (list_length(fc_newstuff) == 1)
		{
			/* 必须检查它不是实用命令 */
			fc_ctequery = linitial_node(Query, fc_newstuff);
			if (!(fc_ctequery->commandType == CMD_SELECT ||
				  fc_ctequery->commandType == CMD_UPDATE ||
				  fc_ctequery->commandType == CMD_INSERT ||
				  fc_ctequery->commandType == CMD_DELETE))
			{
				/*
				 * 目前它只能是 NOTIFY；如果我们将来允许其他实用命令
				 * 在规则中，此错误信息将需要调整。
				 */
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("DO INSTEAD NOTIFY rules are not supported for data-modifying statements in WITH")));
			}
			/* WITH 查询不应为 canSetTag */
			Assert(!fc_ctequery->canSetTag);
			/* 将单个查询推回到 CTE 节点 */
			fc_cte->ctequery = (Node *) fc_ctequery;
		}
		else if (fc_newstuff == NIL)
		{
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("DO INSTEAD NOTHING rules are not supported for data-modifying statements in WITH")));
		}
		else
		{
			ListCell   *fc_lc2;

			/* 检查查询以确定发布哪个错误消息 */
			foreach(fc_lc2, fc_newstuff)
			{
				Query	   *fc_q = (Query *) lfirst(fc_lc2);

				if (fc_q->querySource == QSRC_QUAL_INSTEAD_RULE)
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("conditional DO INSTEAD rules are not supported for data-modifying statements in WITH")));
				if (fc_q->querySource == QSRC_NON_INSTEAD_RULE)
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("DO ALSO rules are not supported for data-modifying statements in WITH")));
			}

			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("multi-statement DO INSTEAD rules are not supported for data-modifying statements in WITH")));
		}
	}

	/*
	 * 如果语句是插入、更新、删除或合并，按需调整其
	 * targetlist，然后在其上触发 INSERT/UPDATE/DELETE 规则。
	 *
	 * SELECT 规则稍后处理，当我们获得所有
	 * 应该执行的查询时。此外，实用程序根本不进行重写（我们
	 * 还需要这个检查吗？）
	 */
	if (fc_event != CMD_SELECT && fc_event != CMD_UTILITY)
	{
		int			fc_result_relation;
		RangeTblEntry *fc_rt_entry;
		Relation	fc_rt_entry_relation;
		List	   *fc_locks;
		int			fc_product_orig_rt_length;
		List	   *fc_product_queries;
		bool		fc_hasUpdate = false;
		int			fc_values_rte_index = 0;
		bool		fc_defaults_remaining = false;

		fc_result_relation = fc_parsetree->resultRelation;
		Assert(fc_result_relation != 0);
		fc_rt_entry = rt_fetch(fc_result_relation, fc_parsetree->rtable);
		Assert(fc_rt_entry->rtekind == RTE_RELATION);

		/*
		 * 由于解析器或 AcquireRewriteLocks 应该已经锁定了关系，
		 * 我们可以在这里使用 NoLock。
		 */
		fc_rt_entry_relation = table_open(fc_rt_entry->relid, NoLock);

		/*
		 * 根据命令类型需要重写目标列表。
		 */
		if (fc_event == CMD_INSERT)
		{
			ListCell   *fc_lc2;
			RangeTblEntry *fc_values_rte = NULL;

			/*
			 * 通过查看 fromlist 中是否存在 VALUES RTE 来测试是否为多行 INSERT ... VALUES (...), (...), ...。对于产品查询，我们必须忽略原始查询中任何已经处理的 VALUES RTE。这些出现在 rangetable 的开始部分。
			 */
			foreach(fc_lc2, fc_parsetree->jointree->fromlist)
			{
				RangeTblRef *fc_rtr = (RangeTblRef *) lfirst(fc_lc2);

				if (IsA(fc_rtr, RangeTblRef) && fc_rtr->rtindex > fc_orig_rt_length)
				{
					RangeTblEntry *fc_rte = rt_fetch(fc_rtr->rtindex,
												  fc_parsetree->rtable);

					if (fc_rte->rtekind == RTE_VALUES)
					{
						/* 不应该发现超过一个 VALUES RTE */
						if (fc_values_rte != NULL)
							elog(ERROR, "more than one VALUES RTE found");

						fc_values_rte = fc_rte;
						fc_values_rte_index = fc_rtr->rtindex;
					}
				}
			}

			if (fc_values_rte)
			{
				Bitmapset  *fc_unused_values_attrnos = NULL;

				/* 处理主要目标列表 ... */
				fc_parsetree->targetList = fc_rewriteTargetListIU(fc_parsetree->targetList,
															fc_parsetree->commandType,
															fc_parsetree->override,
															fc_rt_entry_relation,
															fc_values_rte,
															fc_values_rte_index,
															&fc_unused_values_attrnos);
				/* ... 和 VALUES 表达式列表 */
				if (!fc_rewriteValuesRTE(fc_parsetree, fc_values_rte, fc_values_rte_index,
									  fc_rt_entry_relation,
									  fc_unused_values_attrnos))
					fc_defaults_remaining = true;
			}
			else
			{
				/* 仅处理主要目标列表 */
				fc_parsetree->targetList =
					fc_rewriteTargetListIU(fc_parsetree->targetList,
										fc_parsetree->commandType,
										fc_parsetree->override,
										fc_rt_entry_relation,
										NULL, 0, NULL);
			}

			if (fc_parsetree->onConflict &&
				fc_parsetree->onConflict->action == ONCONFLICT_UPDATE)
			{
				fc_parsetree->onConflict->onConflictSet =
					fc_rewriteTargetListIU(fc_parsetree->onConflict->onConflictSet,
										CMD_UPDATE,
										fc_parsetree->override,
										fc_rt_entry_relation,
										NULL, 0, NULL);
			}
		}
		else if (fc_event == CMD_UPDATE)
		{
			Assert(fc_parsetree->override == OVERRIDING_NOT_SET);
			fc_parsetree->targetList =
				fc_rewriteTargetListIU(fc_parsetree->targetList,
									fc_parsetree->commandType,
									fc_parsetree->override,
									fc_rt_entry_relation,
									NULL, 0, NULL);
		}
		else if (fc_event == CMD_MERGE)
		{
			Assert(fc_parsetree->override == OVERRIDING_NOT_SET);

			/*
			 * 分别重写每个操作的目标列表
			 */
			foreach(fc_lc1, fc_parsetree->mergeActionList)
			{
				MergeAction *fc_action = (MergeAction *) lfirst(fc_lc1);

				switch (fc_action->commandType)
				{
					case CMD_NOTHING:
					case CMD_DELETE:	/* 这里没有事情要做 */
						break;
					case CMD_UPDATE:
					case CMD_INSERT:

						/*
						 * MERGE 操作不允许多行 INSERT，因此这里没有 VALUES RTE 需要处理。
						 */
						fc_action->targetList =
							fc_rewriteTargetListIU(fc_action->targetList,
												fc_action->commandType,
												fc_action->override,
												fc_rt_entry_relation,
												NULL, 0, NULL);
						break;
					default:
						elog(ERROR, "unrecognized commandType: %d", fc_action->commandType);
						break;
				}
			}
		}
		else if (fc_event == CMD_DELETE)
		{
			/* 这里没有事情要做 */
		}
		else
			elog(ERROR, "unrecognized commandType: %d", (int) fc_event);

		/*
		 * 收集并应用适当的规则。
		 */
		fc_locks = fc_matchLocks(fc_event, fc_rt_entry_relation->rd_rules,
						   fc_result_relation, fc_parsetree, &fc_hasUpdate);

		fc_product_orig_rt_length = list_length(fc_parsetree->rtable);
		fc_product_queries = fc_fireRules(fc_parsetree,
									fc_result_relation,
									fc_event,
									fc_locks,
									&fc_instead,
									&fc_returning,
									&fc_qual_product);

		/*
		 * 如果我们有一个包含任何剩余未触及 DEFAULT 项的 VALUES RTE，并且我们得到了任何产品查询，就为每个产品查询最终确定 VALUES RTE（用 NULL 替换剩余的 DEFAULT 项）。我们不这样做原始查询，因为我们知道它必须是在视图上的自动插入，因此应该使用基本关系的默认值来处理任何剩余的 DEFAULT 项。
		 */
		if (fc_defaults_remaining && fc_product_queries != NIL)
		{
			ListCell   *fc_n;

			/*
			 * 每个产品查询在 rangetable 的相同索引上都有自己的一份 VALUES RTE，因此我们必须最终确定每一份。
			 *
			 * 请注意，如果产品查询是 INSERT ... SELECT，那么 VALUES RTE 将位于产品查询的 SELECT 部分的相同索引，而不是顶层产品查询本身。
			 */
			foreach(fc_n, fc_product_queries)
			{
				Query	   *fc_pt = (Query *) lfirst(fc_n);
				RangeTblEntry *fc_values_rte;

				if (fc_pt->commandType == CMD_INSERT &&
					fc_pt->jointree && IsA(fc_pt->jointree, FromExpr) &&
					list_length(fc_pt->jointree->fromlist) == 1)
				{
					Node	   *fc_jtnode = (Node *) linitial(fc_pt->jointree->fromlist);

					if (IsA(fc_jtnode, RangeTblRef))
					{
						int			fc_rtindex = ((RangeTblRef *) fc_jtnode)->rtindex;
						RangeTblEntry *fc_src_rte = rt_fetch(fc_rtindex, fc_pt->rtable);

						if (fc_src_rte->rtekind == RTE_SUBQUERY &&
							fc_src_rte->subquery &&
							IsA(fc_src_rte->subquery, Query) &&
							fc_src_rte->subquery->commandType == CMD_SELECT)
							fc_pt = fc_src_rte->subquery;
					}
				}

				fc_values_rte = rt_fetch(fc_values_rte_index, fc_pt->rtable);
				if (fc_values_rte->rtekind != RTE_VALUES)
					elog(ERROR, "failed to find VALUES RTE in product query");

				fc_rewriteValuesRTEToNulls(fc_pt, fc_values_rte);
			}
		}

		/*
		 * 如果没有未限定的 INSTEAD 规则，并且目标关系是没有任何 INSTEAD OF 触发器的视图，请查看是否可以自动更新该视图。如果可以，我们在这里执行必要的查询转换，并将生成的查询添加到 product_queries 列表中，以便在必要时进行递归重写。
		 *
		 * 如果视图无法自动更新，我们在此抛出一个错误，这是可以的，因为查询在运行时无论如何都会失败。在这里抛出错误优于执行器检查，因为我们有更详细的信息来说明为什么视图不可更新。
		 */
		if (!fc_instead &&
			fc_rt_entry_relation->rd_rel->relkind == RELKIND_VIEW &&
			!fc_view_has_instead_trigger(fc_rt_entry_relation, fc_event))
		{
			/*
			 * 如果存在任何合格的 INSTEAD 规则，不允许自动更新视图（需要未限定的 INSTEAD 规则或 INSTEAD OF 触发器）。
			 *
			 * 这里的消息应该与 execMain.c 的 CheckValidResultRel 相匹配，并原则上使执行器中的这些检查不必要，但我们保留它们以防万一。
			 */
			if (fc_qual_product != NULL)
			{
				switch (fc_parsetree->commandType)
				{
					case CMD_INSERT:
						ereport(ERROR,
								(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
								 errmsg("cannot insert into view \"%s\"",
										RelationGetRelationName(fc_rt_entry_relation)),
								 errdetail("Views with conditional DO INSTEAD rules are not automatically updatable."),
								 errhint("To enable inserting into the view, provide an INSTEAD OF INSERT trigger or an unconditional ON INSERT DO INSTEAD rule.")));
						break;
					case CMD_UPDATE:
						ereport(ERROR,
								(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
								 errmsg("cannot update view \"%s\"",
										RelationGetRelationName(fc_rt_entry_relation)),
								 errdetail("Views with conditional DO INSTEAD rules are not automatically updatable."),
								 errhint("To enable updating the view, provide an INSTEAD OF UPDATE trigger or an unconditional ON UPDATE DO INSTEAD rule.")));
						break;
					case CMD_DELETE:
						ereport(ERROR,
								(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
								 errmsg("cannot delete from view \"%s\"",
										RelationGetRelationName(fc_rt_entry_relation)),
								 errdetail("Views with conditional DO INSTEAD rules are not automatically updatable."),
								 errhint("To enable deleting from the view, provide an INSTEAD OF DELETE trigger or an unconditional ON DELETE DO INSTEAD rule.")));
						break;
					default:
						elog(ERROR, "unrecognized CmdType: %d",
							 (int) fc_parsetree->commandType);
						break;
				}
			}

			/*
			 * 尝试重写查询以自动更新视图。如果视图无法自动更新，则抛出错误。
			 */
			fc_parsetree = fc_rewriteTargetView(fc_parsetree, fc_rt_entry_relation);

			/*
			 * 此时 product_queries 包含任何 DO ALSO 规则的操作。在这些之前或之后添加重写的查询。这必须与原始查询在下面的处理方式相匹配，如果我们允许它再次被包含。
			 */
			if (fc_parsetree->commandType == CMD_INSERT)
				fc_product_queries = lcons(fc_parsetree, fc_product_queries);
			else
				fc_product_queries = lappend(fc_product_queries, fc_parsetree);

			/*
			 * 设置 "instead" 标志，就好像存在一个未限定的 INSTEAD，以防止原始查询在下面被第二次包括。转换将已经重写任何 RETURNING 列表，因此我们也可以设置 "returning" 以防止下面抛出错误。
			 */
			fc_instead = true;
			fc_returning = true;
			fc_updatableview = true;
		}

		/*
		 * 如果我们得到了任何产品查询，递归重写它们 --- 但首先检查递归！
		 */
		if (fc_product_queries != NIL)
		{
			ListCell   *fc_n;
			rewrite_event *fc_rev;

			foreach(fc_n, fc_rewrite_events)
			{
				fc_rev = (rewrite_event *) lfirst(fc_n);
				if (fc_rev->relation == RelationGetRelid(fc_rt_entry_relation) &&
					fc_rev->event == fc_event)
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
							 errmsg("infinite recursion detected in rules for relation \"%s\"",
									RelationGetRelationName(fc_rt_entry_relation))));
			}

			fc_rev = (rewrite_event *) palloc(sizeof(rewrite_event));
			fc_rev->relation = RelationGetRelid(fc_rt_entry_relation);
			fc_rev->event = fc_event;
			fc_rewrite_events = lappend(fc_rewrite_events, fc_rev);

			foreach(fc_n, fc_product_queries)
			{
				Query	   *fc_pt = (Query *) lfirst(fc_n);
				List	   *fc_newstuff;

				/*
				 * 对于可更新视图，pt 可能是原始查询的重写版本，在这种情况下我们将 orig_rt_length 传递给它以完成对其包含的任何 VALUES RTE 的处理。
				 *
				 * 否则，我们有一个由 fireRules() 创建的产品查询。原始查询中的任何 VALUES RTE 都已完全处理，必须在递归时跳过。
				 */
				fc_newstuff = fc_RewriteQuery(fc_pt, fc_rewrite_events,
										fc_pt == fc_parsetree ?
										fc_orig_rt_length :
										fc_product_orig_rt_length);
				fc_rewritten = list_concat(fc_rewritten, fc_newstuff);
			}

			fc_rewrite_events = list_delete_last(fc_rewrite_events);
		}

		/*
		 * 如果存在 INSTEAD，并且原始查询有 RETURNING，则必须在规则中找到 RETURNING，否则失败。（因为 DefineQueryRewrite 仅允许在无条件 INSTEAD 规则中使用 RETURNING，所以不需要担心替换后的 RETURNING 实际上是否会被执行 --- 它必须被执行。）
		 */
		if ((fc_instead || fc_qual_product != NULL) &&
			fc_parsetree->returningList &&
			!fc_returning)
		{
			switch (fc_event)
			{
				case CMD_INSERT:
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("cannot perform INSERT RETURNING on relation \"%s\"",
									RelationGetRelationName(fc_rt_entry_relation)),
							 errhint("You need an unconditional ON INSERT DO INSTEAD rule with a RETURNING clause.")));
					break;
				case CMD_UPDATE:
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("cannot perform UPDATE RETURNING on relation \"%s\"",
									RelationGetRelationName(fc_rt_entry_relation)),
							 errhint("You need an unconditional ON UPDATE DO INSTEAD rule with a RETURNING clause.")));
					break;
				case CMD_DELETE:
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("cannot perform DELETE RETURNING on relation \"%s\"",
									RelationGetRelationName(fc_rt_entry_relation)),
							 errhint("You need an unconditional ON DELETE DO INSTEAD rule with a RETURNING clause.")));
					break;
				default:
					elog(ERROR, "unrecognized commandType: %d",
						 (int) fc_event);
					break;
			}
		}

		/*
		 * 可更新视图受到 ON CONFLICT 的支持，因此不要阻止这种情况的进行
		 */
		if (fc_parsetree->onConflict &&
			(fc_product_queries != NIL || fc_hasUpdate) &&
			!fc_updatableview)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("INSERT with ON CONFLICT clause cannot be used with table that has INSERT or UPDATE rules")));

		table_close(fc_rt_entry_relation, NoLock);
	}

	/*
	 * 对于 INSERT，原始查询首先完成；对于 UPDATE/DELETE，它是最后完成的。
	 * 这是必要的，因为更新和删除规则动作在执行更新或删除后可能不会执行任何操作。
	 * 查询执行之间的命令计数器递增使得被删除的（并且可能被更新的）元组消失，
	 * 因此在规则动作中对此进行扫描无法找到它们。
	 *
	 * 如果我们发现任何未限定的 INSTEAD，原始查询根本不会以任何形式执行。
	 * 否则，如果发现了合格的 INSTEAD，则添加修改后的形式，否则添加未修改的形式。
	 */
	if (!fc_instead)
	{
		if (fc_parsetree->commandType == CMD_INSERT)
		{
			if (fc_qual_product != NULL)
				fc_rewritten = lcons(fc_qual_product, fc_rewritten);
			else
				fc_rewritten = lcons(fc_parsetree, fc_rewritten);
		}
		else
		{
			if (fc_qual_product != NULL)
				fc_rewritten = lappend(fc_rewritten, fc_qual_product);
			else
				fc_rewritten = lappend(fc_rewritten, fc_parsetree);
		}
	}

	
/*
	 * 如果原始查询有一个 CTE 列表，并且我们生成了多个
	 * 非实用结果查询，我们必须失败，因为我们将把 CTE 列表复制到每个结果查询中。
	 * 这将打破对 CTE 的单一评估期望。 
	 * 这可能通过重构来解决，以便 CTE 列表可以在多个查询和可规划语句节点之间共享。
	 */
	if (fc_parsetree->cteList != NIL)
	{
		int			fc_qcount = 0;

		foreach(fc_lc1, fc_rewritten)
		{
			Query	   *fc_q = (Query *) lfirst(fc_lc1);

			if (fc_q->commandType != CMD_UTILITY)
				fc_qcount++;
		}
		if (fc_qcount > 1)
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("WITH cannot be used in a query that is rewritten by rules into multiple queries")));
	}

	return fc_rewritten;
}


/*
 * QueryRewrite -
 *	  查询重写器的主要入口点。
 *	  通过查询重写系统重写一个查询，可能返回 0 或多个查询。
 *
 * 注意：解析树必须直接来自解析器，或已被 AcquireRewriteLocks 扫描以获取适当的锁。
 */
List * QueryRewrite(Query *fc_parsetree)
{
	uint64		fc_input_query_id = fc_parsetree->queryId;
	List	   *fc_querylist;
	List	   *fc_results;
	ListCell   *fc_l;
	CmdType		fc_origCmdType;
	bool		fc_foundOriginalQuery;
	Query	   *fc_lastInstead;

	/*
	 * 此函数仅应用于顶层原始查询
	 */
	Assert(fc_parsetree->querySource == QSRC_ORIGINAL);
	Assert(fc_parsetree->canSetTag);

	/*
	 * 第一步
	 *
	 * 应用所有非SELECT规则，可能得到 0 或多个查询
	 */
	fc_querylist = fc_RewriteQuery(fc_parsetree, NIL, 0);

	/*
	 * 第二步
	 *
	 * 在每个查询上应用所有 RIR 规则
	 *
	 * 这也是标记每个查询带有原始 queryId 的便捷之处
	 */
	fc_results = NIL;
	foreach(fc_l, fc_querylist)
	{
		Query	   *fc_query = (Query *) lfirst(fc_l);

		fc_query = fc_fireRIRrules(fc_query, NIL);

		fc_query->queryId = fc_input_query_id;

		fc_results = lappend(fc_results, fc_query);
	}

	/*
	 * 第三步
	 *
	 * 确定结果查询中是否有任何查询应该设置命令结果标签；并相应地更新 canSetTag 字段。
	 *
	 * 如果原始查询仍在列表中，它设置命令标签。
	 * 否则，最后一个与原始相同类型的 INSTEAD 查询被允许设置标签。
	 * （注意，这些规则可能会导致没有查询设置标签。tcop 代码必须通过基于原始未重写查询设置默认标签来应对这一情况。）
	 *
	 * 断言确认结果列表中最多标记一个查询可以设置标签。
	 * 如果我们不检查断言，一旦找到原始查询，我们可以立即退出循环。
	 */
	fc_origCmdType = fc_parsetree->commandType;
	fc_foundOriginalQuery = false;
	fc_lastInstead = NULL;

	foreach(fc_l, fc_results)
	{
		Query	   *fc_query = (Query *) lfirst(fc_l);

		if (fc_query->querySource == QSRC_ORIGINAL)
		{
			Assert(fc_query->canSetTag);
			Assert(!fc_foundOriginalQuery);
			fc_foundOriginalQuery = true;
#ifndef USE_ASSERT_CHECKING
			break;
#endif
		}
		else
		{
			Assert(!fc_query->canSetTag);
			if (fc_query->commandType == fc_origCmdType &&
				(fc_query->querySource == QSRC_INSTEAD_RULE ||
				 fc_query->querySource == QSRC_QUAL_INSTEAD_RULE))
				fc_lastInstead = fc_query;
		}
	}

	if (!fc_foundOriginalQuery && fc_lastInstead != NULL)
		fc_lastInstead->canSetTag = true;

	return fc_results;
}
